From c4fa9825a9e935d7de5e91cb685a123d376e20b5 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 26 Jun 2020 09:46:14 +0200 Subject: [PATCH 01/46] Mute chat --- protocol/chat.go | 4 + protocol/messenger.go | 40 +++++++ protocol/messenger_mute_test.go | 111 ++++++++++++++++++ protocol/migrations/migrations.go | 60 +++++++++- .../sqlite/1593087212_add_mute_chat.down.sql | 0 .../sqlite/1593087212_add_mute_chat.up.sql | 1 + protocol/persistence.go | 21 +++- services/ext/api.go | 8 ++ 8 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 protocol/messenger_mute_test.go create mode 100644 protocol/migrations/sqlite/1593087212_add_mute_chat.down.sql create mode 100644 protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql diff --git a/protocol/chat.go b/protocol/chat.go index 846324ffb..dcd6124b9 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -64,6 +64,10 @@ type Chat struct { Alias string `json:"alias,omitempty"` // Identicon generated from public key Identicon string `json:"identicon"` + + // Muted is used to check whether we want to receive + // push notifications for this chat + Muted bool `json:"muted,omitempty"` } func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) { diff --git a/protocol/messenger.go b/protocol/messenger.go index 092d18013..82b3763c4 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -2163,6 +2163,46 @@ func (m *Messenger) MarkAllRead(chatID string) error { return nil } +// MuteChat signals to the messenger that we don't want to be notified +// on new messages from this chat +func (m *Messenger) MuteChat(chatID string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + chat, ok := m.allChats[chatID] + if !ok { + return errors.New("chat not found") + } + + err := m.persistence.MuteChat(chatID) + if err != nil { + return err + } + + chat.Muted = true + m.allChats[chat.ID] = chat + return nil +} + +// UnmuteChat signals to the messenger that we want to be notified +// on new messages from this chat +func (m *Messenger) UnmuteChat(chatID string) error { + m.mutex.Lock() + defer m.mutex.Unlock() + chat, ok := m.allChats[chatID] + if !ok { + return errors.New("chat not found") + } + + err := m.persistence.UnmuteChat(chatID) + if err != nil { + return err + } + + chat.Muted = false + m.allChats[chat.ID] = chat + return nil +} + func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error { return m.persistence.UpdateMessageOutgoingStatus(id, newOutgoingStatus) } diff --git a/protocol/messenger_mute_test.go b/protocol/messenger_mute_test.go new file mode 100644 index 000000000..a366088d9 --- /dev/null +++ b/protocol/messenger_mute_test.go @@ -0,0 +1,111 @@ +package protocol + +import ( + "context" + "crypto/ecdsa" + "io/ioutil" + "os" + "testing" + + "github.com/google/uuid" + "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/tt" + "github.com/status-im/status-go/whisper/v6" +) + +func TestMessengerMuteSuite(t *testing.T) { + suite.Run(t, new(MessengerMuteSuite)) +} + +type MessengerMuteSuite struct { + suite.Suite + m *Messenger // main instance of Messenger + privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger + + // If one wants to send messages between different instances of Messenger, + // a single Whisper service should be shared. + shh types.Whisper + + tmpFiles []*os.File // files to clean up + logger *zap.Logger +} + +func (s *MessengerMuteSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := whisper.DefaultConfig + config.MinimumAcceptedPOW = 0 + shh := whisper.New(&config) + s.shh = gethbridge.NewGethWhisperWrapper(shh) + s.Require().NoError(shh.Start(nil)) + + s.m = s.newMessenger(s.shh) + s.privateKey = s.m.identity +} + +func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + + options := []Option{ + WithCustomLogger(s.logger), + WithMessagesPersistenceEnabled(), + WithDatabaseConfig(tmpFile.Name(), "some-key"), + WithDatasync(), + } + installationID := uuid.New().String() + m, err := NewMessenger( + privateKey, + &testNode{shh: shh}, + installationID, + options..., + ) + s.Require().NoError(err) + + err = m.Init() + s.Require().NoError(err) + + s.tmpFiles = append(s.tmpFiles, tmpFile) + + return m +} + +func (s *MessengerMuteSuite) newMessenger(shh types.Whisper) *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + return s.newMessengerWithKey(s.shh, privateKey) +} + +func (s *MessengerMuteSuite) TestSetMute() { + key, err := crypto.GenerateKey() + s.Require().NoError(err) + + theirMessenger := s.newMessengerWithKey(s.shh, key) + + chatID := "status" + + chat := CreatePublicChat(chatID, s.m.transport) + + err = s.m.SaveChat(&chat) + s.Require().NoError(err) + + err = s.m.Join(chat) + s.Require().NoError(err) + + err = theirMessenger.SaveChat(&chat) + s.Require().NoError(err) + + s.Require().NoError(s.m.MuteChat(chatID)) + + s.Require().Len(s.m.Chats(), 1) + s.Require().True(s.m.Chats()[0].Muted) + + s.Require().NoError(s.m.UnmuteChat(chatID)) + s.Require().False(s.m.Chats()[0].Muted) +} diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index e9c4e7aa5..059166b16 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -12,6 +12,8 @@ // 1589365189_add_pow_target.up.sql (66B) // 1591277220_add_index_messages.down.sql (237B) // 1591277220_add_index_messages.up.sql (240B) +// 1593087212_add_mute_chat.down.sql (0) +// 1593087212_add_mute_chat.up.sql (58B) // doc.go (850B) package migrations @@ -216,7 +218,7 @@ func _1588665364_add_image_dataDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)} + info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -236,7 +238,7 @@ func _1588665364_add_image_dataUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)} + info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}} return a, nil } @@ -256,7 +258,7 @@ func _1589365189_add_pow_targetDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)} + info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -276,7 +278,7 @@ func _1589365189_add_pow_targetUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)} + info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}} return a, nil } @@ -296,7 +298,7 @@ func _1591277220_add_index_messagesDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)} + info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x79, 0xe5, 0x42, 0x56, 0x64, 0x1d, 0xb7, 0x8a, 0x1b, 0x0, 0x99, 0xf0, 0x18, 0x8c, 0x69, 0xe3, 0x14, 0x3a, 0x7f, 0x78, 0xfe, 0xe3, 0x2e, 0xcb, 0x6e, 0x5c, 0x8c, 0x1f, 0x7b, 0xfc, 0x21, 0xc7}} return a, nil } @@ -316,11 +318,51 @@ func _1591277220_add_index_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1591361844, 0)} + info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}} return a, nil } +var __1593087212_add_mute_chatDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00") + +func _1593087212_add_mute_chatDownSqlBytes() ([]byte, error) { + return bindataRead( + __1593087212_add_mute_chatDownSql, + "1593087212_add_mute_chat.down.sql", + ) +} + +func _1593087212_add_mute_chatDownSql() (*asset, error) { + bytes, err := _1593087212_add_mute_chatDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1593087310, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} + return a, nil +} + +var __1593087212_add_mute_chatUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xce\x48\x2c\x29\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\xc8\x2d\x2d\x49\x4d\x51\x70\xf2\xf7\xf7\x71\x75\xf4\x53\x70\x71\x75\x73\x0c\xf5\x09\x51\x70\x73\xf4\x09\x76\xb5\xe6\x02\x04\x00\x00\xff\xff\x59\x4c\x4b\xec\x3a\x00\x00\x00") + +func _1593087212_add_mute_chatUpSqlBytes() ([]byte, error) { + return bindataRead( + __1593087212_add_mute_chatUpSql, + "1593087212_add_mute_chat.up.sql", + ) +} + +func _1593087212_add_mute_chatUpSql() (*asset, error) { + bytes, err := _1593087212_add_mute_chatUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1593091669, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe, 0x9, 0xa0, 0x4a, 0x8e, 0x23, 0xe4, 0xce, 0xbc, 0xd4, 0x9, 0xeb, 0xf9, 0x67, 0x90, 0xc0, 0x4b, 0x67, 0x84, 0xe4, 0x42, 0x8d, 0x0, 0x17, 0x29, 0x7f, 0x12, 0xbf, 0x7d, 0x4e, 0x78, 0xec}} + return a, nil +} + var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x3f\x8f\xdb\x3e\x0c\xdd\xf3\x29\x1e\x6e\xb9\xe5\x22\x07\xf8\xfd\xa6\xdb\x3a\x74\xe8\xd2\x2e\xd9\x0b\x46\xa6\x6d\x22\x32\xe5\x8a\xf4\x39\xf9\xf6\x85\x74\x17\x9c\x51\x14\xe8\x4a\x89\x8f\xef\x5f\xd7\xe1\x3c\x89\x61\x90\xc4\x10\x83\x72\x64\x33\x2a\x77\x5c\x38\xd2\x6a\x8c\xa7\x51\x7c\x5a\x2f\x21\xe6\xb9\x33\x27\x5f\xed\x28\x73\x37\xcb\x58\xc8\xb9\x7b\xfb\xff\xe9\xd0\x75\x88\xa4\xcf\x8e\x89\xb4\x4f\xdc\xb0\x0c\xe6\x54\x5c\x74\xc4\x26\x3e\x81\xb0\x14\x1e\xe4\x16\xf0\xc5\x91\x98\xcc\xe1\x13\xf9\xb3\xc1\x27\x46\x24\xe3\x0a\x33\xe4\x82\x31\x1f\x2f\xa2\x3d\x39\x85\x3a\xfa\x36\xec\x26\x95\x61\xa4\x94\xb8\xc7\x50\xf2\xdc\x76\x8d\x66\x46\x2f\x85\xa3\xe7\x72\x7f\x01\x99\xb1\x43\x69\x66\xab\xfb\x13\xbd\x31\x34\x7f\x9c\x07\x69\xff\x6f\x45\xd8\x72\xb9\x1a\xc8\xc0\xb7\x85\xa3\x73\x1f\x0e\x15\xeb\xfb\x8f\xf3\xd7\x57\x9c\x27\xae\xf0\x55\x5a\x1e\x1a\x85\x66\x9e\x32\xf7\x06\xcf\x18\x72\x4a\x79\x6b\x0f\xab\xca\x0d\x2e\x33\x9b\xd3\xbc\x20\x66\x7d\x63\x75\xc9\x5a\xd1\x56\x4d\x72\xe5\xf6\xcf\xb7\x0c\x51\x71\xa1\xf4\xee\x5e\x93\x7e\x7e\x37\xe8\x11\x44\x5c\x4b\x61\xf5\x74\x6f\x2b\xac\xb1\xdc\x97\x8a\x85\x77\xe6\x92\xd5\x9a\xbc\xa5\x64\xcf\x31\xa7\xdd\xbc\xa2\xd9\x44\x85\x3f\x1d\x73\xba\x24\x7e\xc1\x36\x49\x9c\x30\x33\xa9\xb5\x40\xda\x87\x44\xce\xe6\x9f\xfb\x10\x85\x73\x99\xad\x0a\xae\xfc\xaa\xbb\x15\xb3\x16\xe7\x91\xc3\x8e\x50\x33\x7f\xa1\xf8\x51\x85\xc7\x95\xd5\xd8\x40\x7f\x98\xf2\x08\x79\x63\x50\xdf\xe3\x74\x3a\x9d\xfe\xfb\x19\x42\x68\x5d\xe0\x1b\xcd\x4b\xa5\xe9\xb5\xa3\x9b\xa4\x84\x0b\x43\x46\xcd\x85\xfb\xca\x8a\x6f\x62\xad\x64\x31\x09\xab\xd7\xcc\x2a\x5e\x4e\x3d\x97\xaa\x47\xf7\x7a\xfe\x66\x59\x38\x1c\x16\x8a\x57\x1a\x19\xf6\x2b\x89\x73\x0d\x7a\xcc\xaf\x23\x2b\xd7\x3a\xec\xcb\x77\x5c\xae\xe3\xde\xec\x63\x46\x08\xdd\xe7\x20\x8c\x19\xe1\xf0\x3b\x00\x00\xff\xff\x12\xcd\x7f\xc4\x52\x03\x00\x00") func docGoBytes() ([]byte, error) { @@ -456,6 +498,10 @@ var _bindata = map[string]func() (*asset, error){ "1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql, + "1593087212_add_mute_chat.down.sql": _1593087212_add_mute_chatDownSql, + + "1593087212_add_mute_chat.up.sql": _1593087212_add_mute_chatUpSql, + "doc.go": docGo, } @@ -512,6 +558,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}}, "1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}}, "1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}}, + "1593087212_add_mute_chat.down.sql": &bintree{_1593087212_add_mute_chatDownSql, map[string]*bintree{}}, + "1593087212_add_mute_chat.up.sql": &bintree{_1593087212_add_mute_chatUpSql, map[string]*bintree{}}, "doc.go": &bintree{docGo, map[string]*bintree{}}, }} diff --git a/protocol/migrations/sqlite/1593087212_add_mute_chat.down.sql b/protocol/migrations/sqlite/1593087212_add_mute_chat.down.sql new file mode 100644 index 000000000..e69de29bb diff --git a/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql b/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql new file mode 100644 index 000000000..b0c2d5215 --- /dev/null +++ b/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql @@ -0,0 +1 @@ +ALTER TABLE chats ADD COLUMN muted BOOLEAN DEFAULT FALSE; diff --git a/protocol/persistence.go b/protocol/persistence.go index 9ef0720ba..1b0f0bbe0 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -103,8 +103,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { } // Insert record - stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`) + stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates, muted) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)`) if err != nil { return err } @@ -123,6 +123,7 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error { chat.LastMessage, encodedMembers.Bytes(), encodedMembershipUpdates.Bytes(), + chat.Muted, ) if err != nil { return err @@ -136,6 +137,16 @@ func (db sqlitePersistence) DeleteChat(chatID string) error { return err } +func (db sqlitePersistence) MuteChat(chatID string) error { + _, err := db.db.Exec("UPDATE chats SET muted = 1 WHERE id = ?", chatID) + return err +} + +func (db sqlitePersistence) UnmuteChat(chatID string) error { + _, err := db.db.Exec("UPDATE chats SET muted = 0 WHERE id = ?", chatID) + return err +} + func (db sqlitePersistence) Chats() ([]*Chat, error) { return db.chats(nil) } @@ -170,6 +181,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { chats.last_message, chats.members, chats.membership_updates, + chats.muted, contacts.identicon, contacts.alias FROM chats LEFT JOIN contacts ON chats.id = contacts.id @@ -201,6 +213,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) { &chat.LastMessage, &encodedMembers, &encodedMembershipUpdates, + &chat.Muted, &identicon, &alias, ) @@ -251,7 +264,8 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) { last_clock_value, last_message, members, - membership_updates + membership_updates, + muted FROM chats WHERE id = ? `, chatID).Scan(&chat.ID, @@ -266,6 +280,7 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) { &chat.LastMessage, &encodedMembers, &encodedMembershipUpdates, + &chat.Muted, ) switch err { case sql.ErrNoRows: diff --git a/services/ext/api.go b/services/ext/api.go index 7da2a6537..00850e213 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -247,6 +247,14 @@ func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error { return api.service.messenger.DeleteChat(chatID) } +func (api *PublicAPI) MuteChat(parent context.Context, chatID string) error { + return api.service.messenger.MuteChat(chatID) +} + +func (api *PublicAPI) UnmuteChat(parent context.Context, chatID string) error { + return api.service.messenger.UnmuteChat(chatID) +} + func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error { return api.service.messenger.SaveContact(contact) } From 92b699b59d5ecc06859b9acf1c12f3eb529bbba8 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 30 Jun 2020 09:50:59 +0200 Subject: [PATCH 02/46] Add push notification register message --- protocol/applicationmetadata/message.pb.go | 85 -- protocol/applicationmetadata/message.proto | 8 - protocol/applicationmetadata/pubkey.go | 23 - protocol/applicationmetadata/service.go | 17 - protocol/message_processor.go | 34 +- protocol/message_processor_test.go | 1 + protocol/messenger.go | 14 + protocol/messenger_mute_test.go | 1 - .../application_metadata_message.pb.go | 80 +- .../application_metadata_message.proto | 8 + protocol/protobuf/push_notifications.pb.go | 845 ++++++++++++++++++ protocol/protobuf/push_notifications.proto | 91 ++ protocol/protobuf/service.go | 2 +- protocol/push_notification.go | 282 ++++++ protocol/push_notification_persistence.go | 26 + protocol/push_notification_test.go | 219 +++++ 16 files changed, 1568 insertions(+), 168 deletions(-) delete mode 100644 protocol/applicationmetadata/message.pb.go delete mode 100644 protocol/applicationmetadata/message.proto delete mode 100644 protocol/applicationmetadata/pubkey.go delete mode 100644 protocol/applicationmetadata/service.go create mode 100644 protocol/protobuf/push_notifications.pb.go create mode 100644 protocol/protobuf/push_notifications.proto create mode 100644 protocol/push_notification.go create mode 100644 protocol/push_notification_persistence.go create mode 100644 protocol/push_notification_test.go diff --git a/protocol/applicationmetadata/message.pb.go b/protocol/applicationmetadata/message.pb.go deleted file mode 100644 index 4e71857d8..000000000 --- a/protocol/applicationmetadata/message.pb.go +++ /dev/null @@ -1,85 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: message.proto - -package applicationmetadata - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Message struct { - Signature []byte `protobuf:"bytes,4001,opt,name=signature,proto3" json:"signature,omitempty"` - Payload []byte `protobuf:"bytes,4002,opt,name=payload,proto3" json:"payload,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Message) Reset() { *m = Message{} } -func (m *Message) String() string { return proto.CompactTextString(m) } -func (*Message) ProtoMessage() {} -func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_33c57e4bae7b9afd, []int{0} -} - -func (m *Message) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Message.Unmarshal(m, b) -} -func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Message.Marshal(b, m, deterministic) -} -func (m *Message) XXX_Merge(src proto.Message) { - xxx_messageInfo_Message.Merge(m, src) -} -func (m *Message) XXX_Size() int { - return xxx_messageInfo_Message.Size(m) -} -func (m *Message) XXX_DiscardUnknown() { - xxx_messageInfo_Message.DiscardUnknown(m) -} - -var xxx_messageInfo_Message proto.InternalMessageInfo - -func (m *Message) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *Message) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func init() { - proto.RegisterType((*Message)(nil), "applicationmetadata.Message") -} - -func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) } - -var fileDescriptor_33c57e4bae7b9afd = []byte{ - // 112 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e, - 0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4e, 0x2c, 0x28, 0xc8, 0xc9, - 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0xcb, 0x4d, 0x2d, 0x49, 0x4c, 0x49, 0x2c, 0x49, 0x54, 0x72, - 0xe6, 0x62, 0xf7, 0x85, 0xa8, 0x12, 0x92, 0xe5, 0xe2, 0x2c, 0xce, 0x4c, 0xcf, 0x4b, 0x2c, 0x29, - 0x2d, 0x4a, 0x95, 0x58, 0x28, 0xaf, 0xc0, 0xa8, 0xc1, 0x13, 0x84, 0x10, 0x11, 0x92, 0xe4, 0x62, - 0x2f, 0x48, 0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x91, 0x58, 0x04, 0x91, 0x84, 0xf1, 0x93, 0xd8, 0xc0, - 0x16, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x7f, 0x4a, 0x96, 0x71, 0x00, 0x00, 0x00, -} diff --git a/protocol/applicationmetadata/message.proto b/protocol/applicationmetadata/message.proto deleted file mode 100644 index 009830697..000000000 --- a/protocol/applicationmetadata/message.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; - -package applicationmetadata; - -message Message { - bytes signature = 4001; - bytes payload = 4002; -} diff --git a/protocol/applicationmetadata/pubkey.go b/protocol/applicationmetadata/pubkey.go deleted file mode 100644 index 29a152598..000000000 --- a/protocol/applicationmetadata/pubkey.go +++ /dev/null @@ -1,23 +0,0 @@ -package applicationmetadata - -import ( - "crypto/ecdsa" - - "github.com/status-im/status-go/eth-node/crypto" -) - -func (m *Message) RecoverKey() (*ecdsa.PublicKey, error) { - if m.Signature == nil { - return nil, nil - } - - recoveredKey, err := crypto.SigToPub( - crypto.Keccak256(m.Payload), - m.Signature, - ) - if err != nil { - return nil, err - } - - return recoveredKey, nil -} diff --git a/protocol/applicationmetadata/service.go b/protocol/applicationmetadata/service.go deleted file mode 100644 index b3a23b48a..000000000 --- a/protocol/applicationmetadata/service.go +++ /dev/null @@ -1,17 +0,0 @@ -package applicationmetadata - -import ( - "github.com/golang/protobuf/proto" -) - -//go:generate protoc --go_out=. ./message.proto - -func Unmarshal(payload []byte) (*Message, error) { - var message Message - err := proto.Unmarshal(payload, &message) - if err != nil { - return nil, err - } - - return &message, nil -} diff --git a/protocol/message_processor.go b/protocol/message_processor.go index 548534861..d05c38625 100644 --- a/protocol/message_processor.go +++ b/protocol/message_processor.go @@ -42,6 +42,11 @@ type messageProcessor struct { logger *zap.Logger featureFlags featureFlags + // onMessageSpecSent is a callback that is to be called when + // a message spec is sent. + // The reason is a callback is that datasync dispatches things asynchronously + // through a callback, and therefore return values can't be used + onMessageSpecSent func(*ecdsa.PublicKey, *encryption.ProtocolMessageSpec, [][]byte) error } func newMessageProcessor( @@ -51,6 +56,7 @@ func newMessageProcessor( transport transport.Transport, logger *zap.Logger, features featureFlags, + onMessageSpecSent func(*ecdsa.PublicKey, *encryption.ProtocolMessageSpec, [][]byte) error, ) (*messageProcessor, error) { dataSyncTransport := datasync.NewNodeTransport() dataSyncNode, err := datasyncnode.NewPersistentNode( @@ -161,12 +167,13 @@ func (p *messageProcessor) sendPrivate( return nil, errors.Wrap(err, "failed to encrypt message") } - hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec) + messageIDs := [][]byte{messageID} + hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs) if err != nil { return nil, errors.Wrap(err, "failed to send a message spec") } - p.transport.Track([][]byte{messageID}, hash, newMessage) + p.transport.Track(messageIDs, hash, newMessage) } return messageID, nil @@ -190,13 +197,15 @@ func (p *messageProcessor) SendPairInstallation( return nil, errors.Wrap(err, "failed to encrypt message") } - hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec) + messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) + messageIDs := [][]byte{messageID} + + hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs) if err != nil { return nil, errors.Wrap(err, "failed to send a message spec") } - messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) - p.transport.Track([][]byte{messageID}, hash, newMessage) + p.transport.Track(messageIDs, hash, newMessage) return messageID, nil } @@ -330,7 +339,9 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe ctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec) + // We don't pass an array of messageIDs as no action needs to be taken + // when sending a bundle + _, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec, nil) if err != nil { return err } @@ -381,7 +392,7 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu return errors.Wrap(err, "failed to encrypt message") } - hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec) + hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec, messageIDs) if err != nil { return err } @@ -392,7 +403,7 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu } // sendMessageSpec analyses the spec properties and selects a proper transport method. -func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec) ([]byte, *types.NewMessage, error) { +func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) { newMessage, err := messageSpecToWhisper(messageSpec) if err != nil { return nil, nil, err @@ -414,6 +425,13 @@ func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa return nil, nil, err } + if p.onMessageSpecSent != nil { + + if err := p.onMessageSpecSent(publicKey, messageSpec, messageIDs); err != nil { + return nil, nil, err + } + } + return hash, newMessage, nil } diff --git a/protocol/message_processor_test.go b/protocol/message_processor_test.go index 6851e04d4..a2c22bafe 100644 --- a/protocol/message_processor_test.go +++ b/protocol/message_processor_test.go @@ -101,6 +101,7 @@ func (s *MessageProcessorSuite) SetupTest() { whisperTransport, s.logger, featureFlags{}, + nil, ) s.Require().NoError(err) } diff --git a/protocol/messenger.go b/protocol/messenger.go index 82b3763c4..176dabbf8 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -57,6 +57,7 @@ type Messenger struct { encryptor *encryption.Protocol processor *messageProcessor handler *MessageHandler + pushNotificationService *PushNotificationService logger *zap.Logger verifyTransactionClient EthClient featureFlags featureFlags @@ -328,6 +329,9 @@ func NewMessenger( logger, ) + pushNotificationPersistence := NewPushNotificationPersistence(database) + pushNotificationService := NewPushNotificationService(pushNotificationPersistence) + processor, err := newMessageProcessor( identity, database, @@ -335,6 +339,7 @@ func NewMessenger( transp, logger, c.featureFlags, + pushNotificationService.HandleMessageSent, ) if err != nil { return nil, errors.Wrap(err, "failed to create messageProcessor") @@ -350,6 +355,7 @@ func NewMessenger( encryptor: encryptionProtocol, processor: processor, handler: handler, + pushNotificationService: pushNotificationService, featureFlags: c.featureFlags, systemMessagesTranslations: c.systemMessagesTranslations, allChats: make(map[string]*Chat), @@ -1466,6 +1472,14 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes return nil, err } + // If the chat is not public, we instruct the pushNotificationService to send a notification + if !chat.Public() && m.pushNotificationService != nil { + if err := m.pushNotificationService.NotifyOnMessageID(id); err != nil { + return nil, err + } + + } + message.ID = types.EncodeHex(id) err = message.PrepareContent() if err != nil { diff --git a/protocol/messenger_mute_test.go b/protocol/messenger_mute_test.go index a366088d9..fe3fe8b7b 100644 --- a/protocol/messenger_mute_test.go +++ b/protocol/messenger_mute_test.go @@ -1,7 +1,6 @@ package protocol import ( - "context" "crypto/ecdsa" "io/ioutil" "os" diff --git a/protocol/protobuf/application_metadata_message.pb.go b/protocol/protobuf/application_metadata_message.pb.go index b166e283f..30937f2fe 100644 --- a/protocol/protobuf/application_metadata_message.pb.go +++ b/protocol/protobuf/application_metadata_message.pb.go @@ -38,6 +38,14 @@ const ( ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT ApplicationMetadataMessage_Type = 12 ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT ApplicationMetadataMessage_Type = 13 ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT ApplicationMetadataMessage_Type = 14 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTER ApplicationMetadataMessage_Type = 15 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE ApplicationMetadataMessage_Type = 16 + ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT ApplicationMetadataMessage_Type = 17 + ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY ApplicationMetadataMessage_Type = 18 + ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_INFO ApplicationMetadataMessage_Type = 19 + ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE ApplicationMetadataMessage_Type = 20 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST ApplicationMetadataMessage_Type = 21 + ApplicationMetadataMessage_PUSH_NOTIFICATION_ACKNOWLEDGMENT ApplicationMetadataMessage_Type = 22 ) var ApplicationMetadataMessage_Type_name = map[int32]string{ @@ -56,6 +64,14 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{ 12: "SYNC_INSTALLATION_CONTACT", 13: "SYNC_INSTALLATION_ACCOUNT", 14: "SYNC_INSTALLATION_PUBLIC_CHAT", + 15: "PUSH_NOTIFICATION_REGISTER", + 16: "PUSH_NOTIFICATION_REGISTRATION_RESPONSE", + 17: "CONTACT_CODE_ADVERTISEMENT", + 18: "PUSH_NOTIFICATION_QUERY", + 19: "PUSH_NOTIFICATION_QUERY_INFO", + 20: "PUSH_NOTIFICATION_QUERY_RESPONSE", + 21: "PUSH_NOTIFICATION_REQUEST", + 22: "PUSH_NOTIFICATION_ACKNOWLEDGMENT", } var ApplicationMetadataMessage_Type_value = map[string]int32{ @@ -74,6 +90,14 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{ "SYNC_INSTALLATION_CONTACT": 12, "SYNC_INSTALLATION_ACCOUNT": 13, "SYNC_INSTALLATION_PUBLIC_CHAT": 14, + "PUSH_NOTIFICATION_REGISTER": 15, + "PUSH_NOTIFICATION_REGISTRATION_RESPONSE": 16, + "CONTACT_CODE_ADVERTISEMENT": 17, + "PUSH_NOTIFICATION_QUERY": 18, + "PUSH_NOTIFICATION_QUERY_INFO": 19, + "PUSH_NOTIFICATION_QUERY_RESPONSE": 20, + "PUSH_NOTIFICATION_REQUEST": 21, + "PUSH_NOTIFICATION_ACKNOWLEDGMENT": 22, } func (x ApplicationMetadataMessage_Type) String() string { @@ -150,29 +174,35 @@ func init() { func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) } var fileDescriptor_ad09a6406fcf24c7 = []byte{ - // 377 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xcf, 0x8e, 0xd3, 0x30, - 0x10, 0xc6, 0xc9, 0x36, 0x6c, 0x77, 0x67, 0x4b, 0x65, 0x06, 0x10, 0xe1, 0xcf, 0x6a, 0x97, 0x22, - 0xc1, 0x02, 0x52, 0x0e, 0x70, 0xe6, 0xe0, 0x75, 0x0c, 0x1b, 0x91, 0x38, 0xc1, 0x76, 0x84, 0x38, - 0x59, 0x2e, 0x0d, 0x55, 0xa5, 0xb6, 0x89, 0xda, 0xf4, 0xd0, 0x07, 0xe3, 0x29, 0x78, 0x29, 0x94, - 0xd0, 0xd2, 0x96, 0x82, 0x7a, 0xb2, 0xe6, 0xfb, 0x7e, 0x9f, 0x47, 0x33, 0x03, 0x3d, 0x5b, 0x96, - 0xe3, 0xd1, 0x37, 0x5b, 0x8d, 0x8a, 0xa9, 0x99, 0xe4, 0x95, 0x1d, 0xd8, 0xca, 0x9a, 0x49, 0x3e, - 0x9f, 0xdb, 0x61, 0xee, 0x97, 0xb3, 0xa2, 0x2a, 0xf0, 0xa4, 0x79, 0xfa, 0x8b, 0xef, 0xbd, 0x9f, - 0x2e, 0x3c, 0xa6, 0x9b, 0x40, 0xbc, 0xe2, 0xe3, 0xdf, 0x38, 0x3e, 0x85, 0xd3, 0xf9, 0x68, 0x38, - 0xb5, 0xd5, 0x62, 0x96, 0x7b, 0xce, 0xa5, 0x73, 0xd5, 0x91, 0x1b, 0x01, 0x3d, 0x68, 0x97, 0x76, - 0x39, 0x2e, 0xec, 0xc0, 0x3b, 0x6a, 0xbc, 0x75, 0x89, 0xef, 0xc1, 0xad, 0x96, 0x65, 0xee, 0xb5, - 0x2e, 0x9d, 0xab, 0xee, 0xdb, 0x57, 0xfe, 0xba, 0x9f, 0xff, 0xff, 0x5e, 0xbe, 0x5e, 0x96, 0xb9, - 0x6c, 0x62, 0xbd, 0x1f, 0x2d, 0x70, 0xeb, 0x12, 0xcf, 0xa0, 0x9d, 0x89, 0x4f, 0x22, 0xf9, 0x22, - 0xc8, 0x2d, 0x24, 0xd0, 0x61, 0x37, 0x54, 0x9b, 0x98, 0x2b, 0x45, 0x3f, 0x72, 0xe2, 0x20, 0x42, - 0x97, 0x25, 0x42, 0x53, 0xa6, 0x4d, 0x96, 0x06, 0x54, 0x73, 0x72, 0x84, 0xe7, 0xf0, 0x28, 0xe6, - 0xf1, 0x35, 0x97, 0xea, 0x26, 0x4c, 0x57, 0xf2, 0x9f, 0x48, 0x0b, 0x1f, 0xc0, 0xdd, 0x94, 0x86, - 0xd2, 0x84, 0x42, 0x69, 0x1a, 0x45, 0x54, 0x87, 0x89, 0x20, 0x6e, 0x2d, 0xab, 0xaf, 0x82, 0xed, - 0xca, 0xb7, 0xf1, 0x39, 0x5c, 0x48, 0xfe, 0x39, 0xe3, 0x4a, 0x1b, 0x1a, 0x04, 0x92, 0x2b, 0x65, - 0x3e, 0x24, 0xd2, 0x68, 0x49, 0x85, 0xa2, 0xac, 0x81, 0x8e, 0xf1, 0x35, 0xbc, 0xa0, 0x8c, 0xf1, - 0x54, 0x9b, 0x43, 0x6c, 0x1b, 0xdf, 0xc0, 0xcb, 0x80, 0xb3, 0x28, 0x14, 0xfc, 0x20, 0x7c, 0x82, - 0x0f, 0xe1, 0xde, 0x1a, 0xda, 0x36, 0x4e, 0xf1, 0x3e, 0x10, 0xc5, 0x45, 0xb0, 0xa3, 0x02, 0x5e, - 0xc0, 0x93, 0xbf, 0xff, 0xde, 0x06, 0xce, 0xea, 0xd5, 0xec, 0x0d, 0x69, 0x56, 0x0b, 0x24, 0x9d, - 0x7f, 0xdb, 0x94, 0xb1, 0x24, 0x13, 0x9a, 0xdc, 0xc1, 0x67, 0x70, 0xbe, 0x6f, 0xa7, 0xd9, 0x75, - 0x14, 0x32, 0x53, 0xdf, 0x85, 0x74, 0xfb, 0xc7, 0xcd, 0x9d, 0xdf, 0xfd, 0x0a, 0x00, 0x00, 0xff, - 0xff, 0xb7, 0x6c, 0xd6, 0xba, 0x84, 0x02, 0x00, 0x00, + // 479 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcd, 0x52, 0x13, 0x41, + 0x10, 0xc7, 0x0d, 0x1f, 0x09, 0x34, 0x31, 0x0e, 0x0d, 0x48, 0xe4, 0x43, 0x62, 0xb4, 0x14, 0xb5, + 0x2a, 0x07, 0x3d, 0x7b, 0x18, 0x66, 0x3b, 0xc9, 0x94, 0xd9, 0xd9, 0x65, 0x66, 0x56, 0x8b, 0xd3, + 0xd4, 0x22, 0x2b, 0x95, 0x2a, 0x20, 0x5b, 0x64, 0x39, 0xe4, 0x1d, 0x7d, 0x0a, 0x9f, 0xc4, 0xda, + 0x25, 0xe1, 0xc3, 0x24, 0xc5, 0x69, 0xaa, 0xff, 0xff, 0x5f, 0xf7, 0xf4, 0xf4, 0x34, 0x34, 0xe3, + 0x34, 0xbd, 0xe8, 0xff, 0x8a, 0xb3, 0xfe, 0xe0, 0xca, 0x5d, 0x26, 0x59, 0x7c, 0x16, 0x67, 0xb1, + 0xbb, 0x4c, 0x86, 0xc3, 0xf8, 0x3c, 0x69, 0xa5, 0xd7, 0x83, 0x6c, 0x80, 0x2b, 0xc5, 0x71, 0x7a, + 0xf3, 0xbb, 0xf9, 0xb7, 0x0c, 0x3b, 0xfc, 0x3e, 0xc1, 0x1f, 0xf3, 0xfe, 0x2d, 0x8e, 0x7b, 0xb0, + 0x3a, 0xec, 0x9f, 0x5f, 0xc5, 0xd9, 0xcd, 0x75, 0x52, 0x2f, 0x35, 0x4a, 0x87, 0x55, 0x7d, 0x2f, + 0x60, 0x1d, 0x2a, 0x69, 0x3c, 0xba, 0x18, 0xc4, 0x67, 0xf5, 0x85, 0xc2, 0x9b, 0x84, 0xf8, 0x0d, + 0x96, 0xb2, 0x51, 0x9a, 0xd4, 0x17, 0x1b, 0xa5, 0xc3, 0xda, 0x97, 0x8f, 0xad, 0xc9, 0x7d, 0xad, + 0xf9, 0x77, 0xb5, 0xec, 0x28, 0x4d, 0x74, 0x91, 0xd6, 0xfc, 0xb3, 0x0c, 0x4b, 0x79, 0x88, 0x6b, + 0x50, 0x89, 0xd4, 0x77, 0x15, 0xfc, 0x54, 0xec, 0x19, 0x32, 0xa8, 0x8a, 0x2e, 0xb7, 0xce, 0x27, + 0x63, 0x78, 0x87, 0x58, 0x09, 0x11, 0x6a, 0x22, 0x50, 0x96, 0x0b, 0xeb, 0xa2, 0xd0, 0xe3, 0x96, + 0xd8, 0x02, 0xee, 0xc3, 0x2b, 0x9f, 0xfc, 0x23, 0xd2, 0xa6, 0x2b, 0xc3, 0xb1, 0x7c, 0x97, 0xb2, + 0x88, 0x5b, 0xb0, 0x1e, 0x72, 0xa9, 0x9d, 0x54, 0xc6, 0xf2, 0x5e, 0x8f, 0x5b, 0x19, 0x28, 0xb6, + 0x94, 0xcb, 0xe6, 0x44, 0x89, 0xc7, 0xf2, 0x32, 0xbe, 0x85, 0x03, 0x4d, 0xc7, 0x11, 0x19, 0xeb, + 0xb8, 0xe7, 0x69, 0x32, 0xc6, 0xb5, 0x03, 0xed, 0xac, 0xe6, 0xca, 0x70, 0x51, 0x40, 0x65, 0xfc, + 0x04, 0xef, 0xb9, 0x10, 0x14, 0x5a, 0xf7, 0x14, 0x5b, 0xc1, 0xcf, 0xf0, 0xc1, 0x23, 0xd1, 0x93, + 0x8a, 0x9e, 0x84, 0x57, 0x70, 0x1b, 0x36, 0x26, 0xd0, 0x43, 0x63, 0x15, 0x37, 0x81, 0x19, 0x52, + 0xde, 0x23, 0x15, 0xf0, 0x00, 0x76, 0xff, 0xaf, 0xfd, 0x10, 0x58, 0xcb, 0x47, 0x33, 0xf5, 0x48, + 0x37, 0x1e, 0x20, 0xab, 0xce, 0xb6, 0xb9, 0x10, 0x41, 0xa4, 0x2c, 0x7b, 0x8e, 0x6f, 0x60, 0x7f, + 0xda, 0x0e, 0xa3, 0xa3, 0x9e, 0x14, 0x2e, 0xff, 0x17, 0x56, 0xc3, 0xd7, 0xb0, 0x13, 0x46, 0xa6, + 0xeb, 0x54, 0x60, 0x65, 0x5b, 0x8a, 0x5b, 0x44, 0x53, 0x47, 0x1a, 0x4b, 0x9a, 0xbd, 0xc8, 0x5f, + 0x3f, 0xcf, 0xd7, 0x93, 0xc0, 0x84, 0x81, 0x32, 0xc4, 0x58, 0x5e, 0x6c, 0xf2, 0xb9, 0x22, 0xf0, + 0xc8, 0x71, 0xef, 0x07, 0x69, 0x2b, 0x0d, 0xf9, 0xa4, 0x2c, 0x5b, 0xc7, 0x5d, 0xd8, 0x9e, 0x2e, + 0x76, 0x1c, 0x91, 0x3e, 0x61, 0x88, 0x0d, 0xd8, 0x9b, 0x63, 0x3a, 0xa9, 0xda, 0x01, 0xdb, 0xc0, + 0x77, 0xd0, 0x98, 0x47, 0xdc, 0x35, 0xb1, 0x99, 0xcf, 0x64, 0x56, 0xc7, 0xc5, 0x74, 0xd9, 0xd6, + 0xec, 0x22, 0x5c, 0xe4, 0x1b, 0xdb, 0x23, 0xaf, 0x53, 0x74, 0xfa, 0xf2, 0xb4, 0x5c, 0xac, 0xff, + 0xd7, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x68, 0x53, 0x0e, 0x30, 0x9b, 0x03, 0x00, 0x00, } diff --git a/protocol/protobuf/application_metadata_message.proto b/protocol/protobuf/application_metadata_message.proto index dae1d78be..a93f5dac5 100644 --- a/protocol/protobuf/application_metadata_message.proto +++ b/protocol/protobuf/application_metadata_message.proto @@ -27,5 +27,13 @@ message ApplicationMetadataMessage { SYNC_INSTALLATION_CONTACT = 12; SYNC_INSTALLATION_ACCOUNT = 13; SYNC_INSTALLATION_PUBLIC_CHAT = 14; + PUSH_NOTIFICATION_REGISTER = 15; + PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 16; + CONTACT_CODE_ADVERTISEMENT = 17; + PUSH_NOTIFICATION_QUERY = 18; + PUSH_NOTIFICATION_QUERY_INFO = 19; + PUSH_NOTIFICATION_QUERY_RESPONSE = 20; + PUSH_NOTIFICATION_REQUEST = 21; + PUSH_NOTIFICATION_ACKNOWLEDGMENT = 22; } } diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go new file mode 100644 index 000000000..a544d35f7 --- /dev/null +++ b/protocol/protobuf/push_notifications.pb.go @@ -0,0 +1,845 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: push_notifications.proto + +package protobuf + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type PushNotificationOptions_TokenType int32 + +const ( + PushNotificationOptions_UNKNOWN_TOKEN_TYPE PushNotificationOptions_TokenType = 0 + PushNotificationOptions_APN_TOKEN PushNotificationOptions_TokenType = 1 + PushNotificationOptions_FIREBASE_TOKEN PushNotificationOptions_TokenType = 2 +) + +var PushNotificationOptions_TokenType_name = map[int32]string{ + 0: "UNKNOWN_TOKEN_TYPE", + 1: "APN_TOKEN", + 2: "FIREBASE_TOKEN", +} + +var PushNotificationOptions_TokenType_value = map[string]int32{ + "UNKNOWN_TOKEN_TYPE": 0, + "APN_TOKEN": 1, + "FIREBASE_TOKEN": 2, +} + +func (x PushNotificationOptions_TokenType) String() string { + return proto.EnumName(PushNotificationOptions_TokenType_name, int32(x)) +} + +func (PushNotificationOptions_TokenType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{1, 0} +} + +type PushNotificationRegistrationResponse_ErrorType int32 + +const ( + PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE PushNotificationRegistrationResponse_ErrorType = 0 + PushNotificationRegistrationResponse_MALFORMED_MESSAGE PushNotificationRegistrationResponse_ErrorType = 1 + PushNotificationRegistrationResponse_VERSION_MISMATCH PushNotificationRegistrationResponse_ErrorType = 2 + PushNotificationRegistrationResponse_UNSUPPORTED_TOKEN_TYPE PushNotificationRegistrationResponse_ErrorType = 3 + PushNotificationRegistrationResponse_INTERNAL_ERROR PushNotificationRegistrationResponse_ErrorType = 4 +) + +var PushNotificationRegistrationResponse_ErrorType_name = map[int32]string{ + 0: "UNKNOWN_ERROR_TYPE", + 1: "MALFORMED_MESSAGE", + 2: "VERSION_MISMATCH", + 3: "UNSUPPORTED_TOKEN_TYPE", + 4: "INTERNAL_ERROR", +} + +var PushNotificationRegistrationResponse_ErrorType_value = map[string]int32{ + "UNKNOWN_ERROR_TYPE": 0, + "MALFORMED_MESSAGE": 1, + "VERSION_MISMATCH": 2, + "UNSUPPORTED_TOKEN_TYPE": 3, + "INTERNAL_ERROR": 4, +} + +func (x PushNotificationRegistrationResponse_ErrorType) String() string { + return proto.EnumName(PushNotificationRegistrationResponse_ErrorType_name, int32(x)) +} + +func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{4, 0} +} + +type PushNotificationTokenPair struct { + Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationTokenPair) Reset() { *m = PushNotificationTokenPair{} } +func (m *PushNotificationTokenPair) String() string { return proto.CompactTextString(m) } +func (*PushNotificationTokenPair) ProtoMessage() {} +func (*PushNotificationTokenPair) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{0} +} + +func (m *PushNotificationTokenPair) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationTokenPair.Unmarshal(m, b) +} +func (m *PushNotificationTokenPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationTokenPair.Marshal(b, m, deterministic) +} +func (m *PushNotificationTokenPair) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationTokenPair.Merge(m, src) +} +func (m *PushNotificationTokenPair) XXX_Size() int { + return xxx_messageInfo_PushNotificationTokenPair.Size(m) +} +func (m *PushNotificationTokenPair) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationTokenPair.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationTokenPair proto.InternalMessageInfo + +func (m *PushNotificationTokenPair) GetToken() []byte { + if m != nil { + return m.Token + } + return nil +} + +func (m *PushNotificationTokenPair) GetPublicKey() []byte { + if m != nil { + return m.PublicKey + } + return nil +} + +type PushNotificationOptions struct { + TokenType PushNotificationOptions_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationOptions_TokenType" json:"token_type,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + Enabled bool `protobuf:"varint,4,opt,name=enabled,proto3" json:"enabled,omitempty"` + AllowedUserList []*PushNotificationTokenPair `protobuf:"bytes,5,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + BlockedChatList [][]byte `protobuf:"bytes,6,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationOptions) Reset() { *m = PushNotificationOptions{} } +func (m *PushNotificationOptions) String() string { return proto.CompactTextString(m) } +func (*PushNotificationOptions) ProtoMessage() {} +func (*PushNotificationOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{1} +} + +func (m *PushNotificationOptions) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationOptions.Unmarshal(m, b) +} +func (m *PushNotificationOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationOptions.Marshal(b, m, deterministic) +} +func (m *PushNotificationOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationOptions.Merge(m, src) +} +func (m *PushNotificationOptions) XXX_Size() int { + return xxx_messageInfo_PushNotificationOptions.Size(m) +} +func (m *PushNotificationOptions) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationOptions proto.InternalMessageInfo + +func (m *PushNotificationOptions) GetTokenType() PushNotificationOptions_TokenType { + if m != nil { + return m.TokenType + } + return PushNotificationOptions_UNKNOWN_TOKEN_TYPE +} + +func (m *PushNotificationOptions) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +func (m *PushNotificationOptions) GetInstallationId() string { + if m != nil { + return m.InstallationId + } + return "" +} + +func (m *PushNotificationOptions) GetEnabled() bool { + if m != nil { + return m.Enabled + } + return false +} + +func (m *PushNotificationOptions) GetAllowedUserList() []*PushNotificationTokenPair { + if m != nil { + return m.AllowedUserList + } + return nil +} + +func (m *PushNotificationOptions) GetBlockedChatList() [][]byte { + if m != nil { + return m.BlockedChatList + } + return nil +} + +type PushNotificationPreferences struct { + Options []*PushNotificationOptions `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` + Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + Unregister bool `protobuf:"varint,3,opt,name=unregister,proto3" json:"unregister,omitempty"` + AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationPreferences) Reset() { *m = PushNotificationPreferences{} } +func (m *PushNotificationPreferences) String() string { return proto.CompactTextString(m) } +func (*PushNotificationPreferences) ProtoMessage() {} +func (*PushNotificationPreferences) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{2} +} + +func (m *PushNotificationPreferences) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationPreferences.Unmarshal(m, b) +} +func (m *PushNotificationPreferences) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationPreferences.Marshal(b, m, deterministic) +} +func (m *PushNotificationPreferences) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationPreferences.Merge(m, src) +} +func (m *PushNotificationPreferences) XXX_Size() int { + return xxx_messageInfo_PushNotificationPreferences.Size(m) +} +func (m *PushNotificationPreferences) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationPreferences.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationPreferences proto.InternalMessageInfo + +func (m *PushNotificationPreferences) GetOptions() []*PushNotificationOptions { + if m != nil { + return m.Options + } + return nil +} + +func (m *PushNotificationPreferences) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *PushNotificationPreferences) GetUnregister() bool { + if m != nil { + return m.Unregister + } + return false +} + +func (m *PushNotificationPreferences) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +type PushNotificationRegister struct { + Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationRegister) Reset() { *m = PushNotificationRegister{} } +func (m *PushNotificationRegister) String() string { return proto.CompactTextString(m) } +func (*PushNotificationRegister) ProtoMessage() {} +func (*PushNotificationRegister) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{3} +} + +func (m *PushNotificationRegister) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationRegister.Unmarshal(m, b) +} +func (m *PushNotificationRegister) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationRegister.Marshal(b, m, deterministic) +} +func (m *PushNotificationRegister) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationRegister.Merge(m, src) +} +func (m *PushNotificationRegister) XXX_Size() int { + return xxx_messageInfo_PushNotificationRegister.Size(m) +} +func (m *PushNotificationRegister) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationRegister.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationRegister proto.InternalMessageInfo + +func (m *PushNotificationRegister) GetPayload() []byte { + if m != nil { + return m.Payload + } + return nil +} + +func (m *PushNotificationRegister) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +type PushNotificationRegistrationResponse struct { + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` + RequestId []byte `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Preferences *PushNotificationPreferences `protobuf:"bytes,4,opt,name=preferences,proto3" json:"preferences,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotificationRegistrationResponse{} } +func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) } +func (*PushNotificationRegistrationResponse) ProtoMessage() {} +func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{4} +} + +func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationRegistrationResponse.Unmarshal(m, b) +} +func (m *PushNotificationRegistrationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationRegistrationResponse.Marshal(b, m, deterministic) +} +func (m *PushNotificationRegistrationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationRegistrationResponse.Merge(m, src) +} +func (m *PushNotificationRegistrationResponse) XXX_Size() int { + return xxx_messageInfo_PushNotificationRegistrationResponse.Size(m) +} +func (m *PushNotificationRegistrationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationRegistrationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationRegistrationResponse proto.InternalMessageInfo + +func (m *PushNotificationRegistrationResponse) GetSuccess() bool { + if m != nil { + return m.Success + } + return false +} + +func (m *PushNotificationRegistrationResponse) GetError() PushNotificationRegistrationResponse_ErrorType { + if m != nil { + return m.Error + } + return PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE +} + +func (m *PushNotificationRegistrationResponse) GetRequestId() []byte { + if m != nil { + return m.RequestId + } + return nil +} + +func (m *PushNotificationRegistrationResponse) GetPreferences() *PushNotificationPreferences { + if m != nil { + return m.Preferences + } + return nil +} + +type PushNotificationAdvertisementInfo struct { + PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationAdvertisementInfo) Reset() { *m = PushNotificationAdvertisementInfo{} } +func (m *PushNotificationAdvertisementInfo) String() string { return proto.CompactTextString(m) } +func (*PushNotificationAdvertisementInfo) ProtoMessage() {} +func (*PushNotificationAdvertisementInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{5} +} + +func (m *PushNotificationAdvertisementInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationAdvertisementInfo.Unmarshal(m, b) +} +func (m *PushNotificationAdvertisementInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationAdvertisementInfo.Marshal(b, m, deterministic) +} +func (m *PushNotificationAdvertisementInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationAdvertisementInfo.Merge(m, src) +} +func (m *PushNotificationAdvertisementInfo) XXX_Size() int { + return xxx_messageInfo_PushNotificationAdvertisementInfo.Size(m) +} +func (m *PushNotificationAdvertisementInfo) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationAdvertisementInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationAdvertisementInfo proto.InternalMessageInfo + +func (m *PushNotificationAdvertisementInfo) GetPublicKey() []byte { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *PushNotificationAdvertisementInfo) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +func (m *PushNotificationAdvertisementInfo) GetInstallationId() string { + if m != nil { + return m.InstallationId + } + return "" +} + +type ContactCodeAdvertisement struct { + PushNotificationInfo []*PushNotificationAdvertisementInfo `protobuf:"bytes,1,rep,name=push_notification_info,json=pushNotificationInfo,proto3" json:"push_notification_info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertisement{} } +func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) } +func (*ContactCodeAdvertisement) ProtoMessage() {} +func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{6} +} + +func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ContactCodeAdvertisement.Unmarshal(m, b) +} +func (m *ContactCodeAdvertisement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ContactCodeAdvertisement.Marshal(b, m, deterministic) +} +func (m *ContactCodeAdvertisement) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContactCodeAdvertisement.Merge(m, src) +} +func (m *ContactCodeAdvertisement) XXX_Size() int { + return xxx_messageInfo_ContactCodeAdvertisement.Size(m) +} +func (m *ContactCodeAdvertisement) XXX_DiscardUnknown() { + xxx_messageInfo_ContactCodeAdvertisement.DiscardUnknown(m) +} + +var xxx_messageInfo_ContactCodeAdvertisement proto.InternalMessageInfo + +func (m *ContactCodeAdvertisement) GetPushNotificationInfo() []*PushNotificationAdvertisementInfo { + if m != nil { + return m.PushNotificationInfo + } + return nil +} + +type PushNotificationQuery struct { + PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} } +func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) } +func (*PushNotificationQuery) ProtoMessage() {} +func (*PushNotificationQuery) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{7} +} + +func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationQuery.Unmarshal(m, b) +} +func (m *PushNotificationQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationQuery.Marshal(b, m, deterministic) +} +func (m *PushNotificationQuery) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationQuery.Merge(m, src) +} +func (m *PushNotificationQuery) XXX_Size() int { + return xxx_messageInfo_PushNotificationQuery.Size(m) +} +func (m *PushNotificationQuery) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationQuery.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationQuery proto.InternalMessageInfo + +func (m *PushNotificationQuery) GetPublicKeys() [][]byte { + if m != nil { + return m.PublicKeys + } + return nil +} + +type PushNotificationQueryInfo struct { + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + AllowedUserList []byte `protobuf:"bytes,4,opt,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQueryInfo{} } +func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) } +func (*PushNotificationQueryInfo) ProtoMessage() {} +func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{8} +} + +func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationQueryInfo.Unmarshal(m, b) +} +func (m *PushNotificationQueryInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationQueryInfo.Marshal(b, m, deterministic) +} +func (m *PushNotificationQueryInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationQueryInfo.Merge(m, src) +} +func (m *PushNotificationQueryInfo) XXX_Size() int { + return xxx_messageInfo_PushNotificationQueryInfo.Size(m) +} +func (m *PushNotificationQueryInfo) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationQueryInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationQueryInfo proto.InternalMessageInfo + +func (m *PushNotificationQueryInfo) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +func (m *PushNotificationQueryInfo) GetInstallationId() string { + if m != nil { + return m.InstallationId + } + return "" +} + +func (m *PushNotificationQueryInfo) GetPublicKey() []byte { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *PushNotificationQueryInfo) GetAllowedUserList() []byte { + if m != nil { + return m.AllowedUserList + } + return nil +} + +type PushNotificationQueryResponse struct { + Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQueryResponse{} } +func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) } +func (*PushNotificationQueryResponse) ProtoMessage() {} +func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{9} +} + +func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationQueryResponse.Unmarshal(m, b) +} +func (m *PushNotificationQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationQueryResponse.Marshal(b, m, deterministic) +} +func (m *PushNotificationQueryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationQueryResponse.Merge(m, src) +} +func (m *PushNotificationQueryResponse) XXX_Size() int { + return xxx_messageInfo_PushNotificationQueryResponse.Size(m) +} +func (m *PushNotificationQueryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationQueryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationQueryResponse proto.InternalMessageInfo + +func (m *PushNotificationQueryResponse) GetInfo() []*PushNotificationQueryInfo { + if m != nil { + return m.Info + } + return nil +} + +type PushNotification struct { + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotification) Reset() { *m = PushNotification{} } +func (m *PushNotification) String() string { return proto.CompactTextString(m) } +func (*PushNotification) ProtoMessage() {} +func (*PushNotification) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{10} +} + +func (m *PushNotification) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotification.Unmarshal(m, b) +} +func (m *PushNotification) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotification.Marshal(b, m, deterministic) +} +func (m *PushNotification) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotification.Merge(m, src) +} +func (m *PushNotification) XXX_Size() int { + return xxx_messageInfo_PushNotification.Size(m) +} +func (m *PushNotification) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotification.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotification proto.InternalMessageInfo + +func (m *PushNotification) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +func (m *PushNotification) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +type PushNotificationRequest struct { + Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"` + Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + MessageId string `protobuf:"bytes,3,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + AckRequired string `protobuf:"bytes,4,opt,name=ack_required,json=ackRequired,proto3" json:"ack_required,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest{} } +func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) } +func (*PushNotificationRequest) ProtoMessage() {} +func (*PushNotificationRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{11} +} + +func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationRequest.Unmarshal(m, b) +} +func (m *PushNotificationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationRequest.Marshal(b, m, deterministic) +} +func (m *PushNotificationRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationRequest.Merge(m, src) +} +func (m *PushNotificationRequest) XXX_Size() int { + return xxx_messageInfo_PushNotificationRequest.Size(m) +} +func (m *PushNotificationRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationRequest proto.InternalMessageInfo + +func (m *PushNotificationRequest) GetRequests() []*PushNotification { + if m != nil { + return m.Requests + } + return nil +} + +func (m *PushNotificationRequest) GetMessage() []byte { + if m != nil { + return m.Message + } + return nil +} + +func (m *PushNotificationRequest) GetMessageId() string { + if m != nil { + return m.MessageId + } + return "" +} + +func (m *PushNotificationRequest) GetAckRequired() string { + if m != nil { + return m.AckRequired + } + return "" +} + +type PushNotificationAcknowledgement struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationAcknowledgement) Reset() { *m = PushNotificationAcknowledgement{} } +func (m *PushNotificationAcknowledgement) String() string { return proto.CompactTextString(m) } +func (*PushNotificationAcknowledgement) ProtoMessage() {} +func (*PushNotificationAcknowledgement) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{12} +} + +func (m *PushNotificationAcknowledgement) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationAcknowledgement.Unmarshal(m, b) +} +func (m *PushNotificationAcknowledgement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationAcknowledgement.Marshal(b, m, deterministic) +} +func (m *PushNotificationAcknowledgement) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationAcknowledgement.Merge(m, src) +} +func (m *PushNotificationAcknowledgement) XXX_Size() int { + return xxx_messageInfo_PushNotificationAcknowledgement.Size(m) +} +func (m *PushNotificationAcknowledgement) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationAcknowledgement.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationAcknowledgement proto.InternalMessageInfo + +func (m *PushNotificationAcknowledgement) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func init() { + proto.RegisterEnum("protobuf.PushNotificationOptions_TokenType", PushNotificationOptions_TokenType_name, PushNotificationOptions_TokenType_value) + proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value) + proto.RegisterType((*PushNotificationTokenPair)(nil), "protobuf.PushNotificationTokenPair") + proto.RegisterType((*PushNotificationOptions)(nil), "protobuf.PushNotificationOptions") + proto.RegisterType((*PushNotificationPreferences)(nil), "protobuf.PushNotificationPreferences") + proto.RegisterType((*PushNotificationRegister)(nil), "protobuf.PushNotificationRegister") + proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") + proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") + proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement") + proto.RegisterType((*PushNotificationQuery)(nil), "protobuf.PushNotificationQuery") + proto.RegisterType((*PushNotificationQueryInfo)(nil), "protobuf.PushNotificationQueryInfo") + proto.RegisterType((*PushNotificationQueryResponse)(nil), "protobuf.PushNotificationQueryResponse") + proto.RegisterType((*PushNotification)(nil), "protobuf.PushNotification") + proto.RegisterType((*PushNotificationRequest)(nil), "protobuf.PushNotificationRequest") + proto.RegisterType((*PushNotificationAcknowledgement)(nil), "protobuf.PushNotificationAcknowledgement") +} + +func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } + +var fileDescriptor_200acd86044eaa5d = []byte{ + // 876 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdf, 0x6f, 0xe3, 0x44, + 0x10, 0xc6, 0x49, 0xda, 0xc6, 0x93, 0x90, 0xa6, 0xab, 0x5e, 0xcf, 0x1c, 0x1c, 0xd7, 0x1a, 0x10, + 0xd5, 0x21, 0x45, 0xa2, 0x48, 0x70, 0x12, 0x4f, 0xa1, 0xe7, 0x1e, 0xa6, 0x8d, 0x13, 0x36, 0x29, + 0x3f, 0x9e, 0x2c, 0xc7, 0xde, 0xb4, 0xab, 0xf8, 0xbc, 0x66, 0x77, 0x7d, 0xa7, 0x3c, 0x20, 0x21, + 0xf1, 0xce, 0x7f, 0xc2, 0x0b, 0x2f, 0xfc, 0x71, 0xbc, 0x20, 0xaf, 0x7f, 0xd4, 0x75, 0xd2, 0x5e, + 0x9f, 0xe2, 0x99, 0xdd, 0x99, 0x9d, 0xf9, 0xe6, 0x9b, 0x2f, 0x60, 0xc4, 0x89, 0xb8, 0x76, 0x23, + 0x26, 0xe9, 0x82, 0xfa, 0x9e, 0xa4, 0x2c, 0x12, 0x83, 0x98, 0x33, 0xc9, 0x50, 0x5b, 0xfd, 0xcc, + 0x93, 0x85, 0x39, 0x81, 0x0f, 0x26, 0x89, 0xb8, 0x76, 0x2a, 0x97, 0x66, 0x6c, 0x49, 0xa2, 0x89, + 0x47, 0x39, 0xda, 0x87, 0x2d, 0x99, 0x1a, 0x86, 0x76, 0xa8, 0x1d, 0x77, 0x71, 0x66, 0xa0, 0xa7, + 0x00, 0x71, 0x32, 0x0f, 0xa9, 0xef, 0x2e, 0xc9, 0xca, 0x68, 0xa8, 0x23, 0x3d, 0xf3, 0x9c, 0x93, + 0x95, 0xf9, 0x67, 0x13, 0x1e, 0xd7, 0x53, 0x8e, 0x63, 0xf5, 0x3a, 0xfa, 0x01, 0x40, 0xe5, 0x70, + 0xe5, 0x2a, 0x26, 0x2a, 0x6b, 0xef, 0xe4, 0x8b, 0x41, 0x51, 0xcc, 0xe0, 0x8e, 0xb0, 0x81, 0xaa, + 0x68, 0xb6, 0x8a, 0x09, 0xd6, 0x65, 0xf1, 0x79, 0x53, 0x5c, 0x5a, 0x81, 0x5e, 0x14, 0xf7, 0x39, + 0xec, 0xd2, 0x48, 0x48, 0x2f, 0x0c, 0x55, 0x06, 0x97, 0x06, 0x46, 0x53, 0x9d, 0xf7, 0xaa, 0x6e, + 0x3b, 0x40, 0x06, 0xec, 0x90, 0xc8, 0x9b, 0x87, 0x24, 0x30, 0x5a, 0x87, 0xda, 0x71, 0x1b, 0x17, + 0x26, 0x1a, 0xc3, 0x9e, 0x17, 0x86, 0xec, 0x2d, 0x09, 0xdc, 0x44, 0x10, 0xee, 0x86, 0x54, 0x48, + 0x63, 0xeb, 0xb0, 0x79, 0xdc, 0x39, 0xf9, 0xe4, 0xee, 0x5a, 0x4b, 0xd4, 0xf0, 0x6e, 0x1e, 0x7d, + 0x29, 0x08, 0xbf, 0xa0, 0x42, 0xa2, 0xe7, 0xb0, 0x37, 0x0f, 0x99, 0xbf, 0x24, 0x81, 0xeb, 0x5f, + 0x7b, 0x32, 0x4b, 0xb8, 0x7d, 0xd8, 0x3c, 0xee, 0xe2, 0xdd, 0xfc, 0xe0, 0xf4, 0xda, 0x93, 0xe9, + 0x5d, 0xf3, 0x0c, 0xf4, 0xb2, 0x5b, 0x74, 0x00, 0xe8, 0xd2, 0x39, 0x77, 0xc6, 0x3f, 0x3b, 0xee, + 0x6c, 0x7c, 0x6e, 0x39, 0xee, 0xec, 0xd7, 0x89, 0xd5, 0x7f, 0x0f, 0xbd, 0x0f, 0xfa, 0x70, 0x92, + 0xfb, 0xfa, 0x1a, 0x42, 0xd0, 0x3b, 0xb3, 0xb1, 0xf5, 0xdd, 0x70, 0x6a, 0xe5, 0xbe, 0x86, 0xf9, + 0xaf, 0x06, 0x1f, 0xd6, 0x4b, 0x9c, 0x70, 0xb2, 0x20, 0x9c, 0x44, 0x3e, 0x11, 0xe8, 0x5b, 0xd8, + 0x61, 0x19, 0xba, 0x86, 0xa6, 0x5a, 0x3b, 0x7a, 0xe7, 0x18, 0x70, 0x11, 0x91, 0x62, 0xf7, 0x86, + 0x70, 0x41, 0x59, 0x06, 0x7e, 0x0b, 0x17, 0x26, 0xfa, 0x18, 0x20, 0x89, 0x38, 0xb9, 0xa2, 0x42, + 0x12, 0xae, 0x90, 0x6f, 0xe3, 0x8a, 0x07, 0x1d, 0x41, 0xd7, 0xf3, 0x7d, 0x22, 0x84, 0x9b, 0xcd, + 0xae, 0xa5, 0x66, 0xd3, 0xc9, 0x7c, 0xaa, 0x71, 0x13, 0x83, 0x51, 0x2f, 0x00, 0x17, 0xe1, 0x06, + 0xec, 0xc4, 0xde, 0x2a, 0x64, 0x5e, 0x90, 0x53, 0xb2, 0x30, 0xd1, 0x47, 0xa0, 0x0b, 0x7a, 0x15, + 0x79, 0x32, 0xe1, 0xa4, 0xe0, 0x64, 0xe9, 0x30, 0xff, 0x6b, 0xc0, 0xa7, 0x9b, 0x93, 0xf2, 0xfc, + 0x5b, 0xc4, 0x2c, 0x12, 0x24, 0x7d, 0x40, 0x24, 0xaa, 0x18, 0xf5, 0x40, 0x1b, 0x17, 0x26, 0x72, + 0x60, 0x8b, 0x70, 0xce, 0xb8, 0x4a, 0xde, 0x3b, 0x79, 0x71, 0x37, 0x5c, 0x9b, 0x12, 0x0f, 0xac, + 0x34, 0x56, 0x51, 0x38, 0x4b, 0x93, 0x6e, 0x11, 0x27, 0xbf, 0x25, 0x44, 0xc8, 0x82, 0xa3, 0x5d, + 0xac, 0xe7, 0x1e, 0x3b, 0x40, 0xaf, 0xa0, 0x13, 0xdf, 0x8c, 0x4b, 0xe1, 0xd4, 0x39, 0xf9, 0xec, + 0xee, 0x47, 0x2b, 0xb3, 0xc5, 0xd5, 0x48, 0xf3, 0x0f, 0x0d, 0xf4, 0xf2, 0xf1, 0x2a, 0xa3, 0x2c, + 0x8c, 0xc7, 0xb8, 0x60, 0xd4, 0x23, 0xd8, 0x1b, 0x0d, 0x2f, 0xce, 0xc6, 0x78, 0x64, 0xbd, 0x74, + 0x47, 0xd6, 0x74, 0x3a, 0x7c, 0x65, 0xf5, 0x35, 0xb4, 0x0f, 0xfd, 0x9f, 0x2c, 0x3c, 0xb5, 0xc7, + 0x8e, 0x3b, 0xb2, 0xa7, 0xa3, 0xe1, 0xec, 0xf4, 0xfb, 0x7e, 0x03, 0x3d, 0x81, 0x83, 0x4b, 0x67, + 0x7a, 0x39, 0x99, 0x8c, 0xf1, 0xcc, 0x7a, 0x59, 0xa5, 0x66, 0x33, 0xe5, 0xa2, 0xed, 0xcc, 0x2c, + 0xec, 0x0c, 0x2f, 0xb2, 0x17, 0xfa, 0x2d, 0xf3, 0x2f, 0x0d, 0x8e, 0xea, 0xf5, 0x0e, 0x83, 0x37, + 0x84, 0x4b, 0x2a, 0xc8, 0x6b, 0x12, 0x49, 0x3b, 0x5a, 0xb0, 0x9a, 0xac, 0x68, 0x35, 0x59, 0x59, + 0x63, 0x4e, 0x63, 0x8d, 0x39, 0x0f, 0xde, 0x7d, 0xf3, 0x77, 0x30, 0x4e, 0x59, 0x24, 0x3d, 0x5f, + 0x9e, 0xb2, 0x80, 0xdc, 0x2a, 0x05, 0x79, 0x70, 0xb0, 0x26, 0x9b, 0x2e, 0x8d, 0x16, 0x2c, 0xdf, + 0x93, 0x7b, 0xe4, 0x6a, 0xad, 0x27, 0xbc, 0x1f, 0xd7, 0xae, 0xa4, 0x5e, 0xf3, 0x05, 0x3c, 0xaa, + 0x87, 0xfe, 0x98, 0x10, 0xbe, 0x42, 0xcf, 0xa0, 0x73, 0x03, 0x41, 0xb6, 0x98, 0x5d, 0x0c, 0x25, + 0x06, 0xc2, 0xfc, 0x47, 0x5b, 0x97, 0x6b, 0x15, 0xaa, 0x10, 0xac, 0x43, 0xa4, 0x3d, 0x08, 0xa2, + 0xc6, 0x46, 0x79, 0xbc, 0x3d, 0x8d, 0x66, 0x7d, 0x1a, 0xcf, 0x37, 0x69, 0x64, 0x4b, 0xdd, 0xaa, + 0xcb, 0x9f, 0xf9, 0x0b, 0x3c, 0xdd, 0x58, 0x73, 0xb9, 0x74, 0xdf, 0x40, 0xab, 0x02, 0xf0, 0x3d, + 0x1a, 0x5b, 0xb6, 0x8a, 0x55, 0x80, 0xe9, 0x40, 0xbf, 0x7e, 0xe5, 0x21, 0x20, 0x3c, 0x86, 0x1d, + 0xa5, 0xc3, 0x65, 0xf3, 0xdb, 0xa9, 0x69, 0x07, 0xe6, 0xdf, 0xda, 0xfa, 0x5f, 0x17, 0xce, 0x56, + 0x12, 0x7d, 0x0d, 0xed, 0x7c, 0x3b, 0x0b, 0xc5, 0x7c, 0x72, 0x8f, 0x04, 0x94, 0x77, 0x53, 0x45, + 0x79, 0x4d, 0x84, 0xf0, 0xae, 0x0a, 0x59, 0x2a, 0xcc, 0x14, 0xe2, 0xfc, 0xf3, 0x86, 0xa9, 0x7a, + 0xee, 0xb1, 0x83, 0xac, 0x91, 0xa5, 0x9b, 0x26, 0xa2, 0x3c, 0xff, 0x97, 0x52, 0x8d, 0x2c, 0x71, + 0xee, 0x32, 0xbf, 0x84, 0x67, 0x6b, 0x1c, 0xf4, 0x97, 0x11, 0x7b, 0x1b, 0x92, 0xe0, 0x2a, 0xa3, + 0x73, 0x0f, 0x1a, 0x34, 0xc8, 0x41, 0x68, 0xd0, 0x60, 0xbe, 0xad, 0x6a, 0xfe, 0xea, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xc3, 0x71, 0xe1, 0x4f, 0x1c, 0x08, 0x00, 0x00, +} diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto new file mode 100644 index 000000000..db0795bb9 --- /dev/null +++ b/protocol/protobuf/push_notifications.proto @@ -0,0 +1,91 @@ +syntax = "proto3"; + +package protobuf; + +message PushNotificationTokenPair { + bytes token = 1; + bytes public_key = 2; +} + +message PushNotificationOptions { + enum TokenType { + UNKNOWN_TOKEN_TYPE = 0; + APN_TOKEN = 1; + FIREBASE_TOKEN = 2; + } + TokenType token_type = 1; + string token = 2; + string installation_id = 3; + bool enabled = 4; + repeated PushNotificationTokenPair allowed_user_list = 5; + repeated bytes blocked_chat_list = 6; +} + +message PushNotificationPreferences { + repeated PushNotificationOptions options = 1; + uint64 version = 2; + bool unregister = 3; + string access_token = 4; +} + +message PushNotificationRegister { + bytes payload = 1; + bytes signature = 2; +} + + +message PushNotificationRegistrationResponse { + bool success = 1; + ErrorType error = 2; + bytes request_id = 3; + PushNotificationPreferences preferences = 4; + + enum ErrorType { + UNKNOWN_ERROR_TYPE = 0; + MALFORMED_MESSAGE = 1; + VERSION_MISMATCH = 2; + UNSUPPORTED_TOKEN_TYPE = 3; + INTERNAL_ERROR = 4; + } +} + +message PushNotificationAdvertisementInfo { + bytes public_key = 1; + string access_token = 2; + string installation_id = 3; +} + +message ContactCodeAdvertisement { + repeated PushNotificationAdvertisementInfo push_notification_info = 1; +} + +message PushNotificationQuery { + repeated bytes public_keys = 1; +} + +message PushNotificationQueryInfo { + string access_token = 1; + string installation_id = 2; + bytes public_key = 3; + bytes allowed_user_list = 4; +} + +message PushNotificationQueryResponse { + repeated PushNotificationQueryInfo info = 1; +} + +message PushNotification { + string access_token = 1; + string chat_id = 2; +} + +message PushNotificationRequest { + repeated PushNotification requests = 1; + bytes message = 2; + string message_id = 3; + string ack_required = 4; +} + +message PushNotificationAcknowledgement { + string id = 1; +} diff --git a/protocol/protobuf/service.go b/protocol/protobuf/service.go index f6652e485..12d13e972 100644 --- a/protocol/protobuf/service.go +++ b/protocol/protobuf/service.go @@ -4,7 +4,7 @@ import ( "github.com/golang/protobuf/proto" ) -//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto +//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto ./push_notifications.proto func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) { var message ApplicationMetadataMessage diff --git a/protocol/push_notification.go b/protocol/push_notification.go new file mode 100644 index 000000000..5d57233cf --- /dev/null +++ b/protocol/push_notification.go @@ -0,0 +1,282 @@ +package protocol + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/rand" + "io" + + "golang.org/x/crypto/sha3" + + "github.com/golang/protobuf/proto" + + "github.com/google/uuid" + + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/protocol/encryption" + "github.com/status-im/status-go/protocol/protobuf" +) + +const accessTokenKeyLength = 16 + +type PushNotificationServer struct { + key *ecdsa.PublicKey + registered bool +} + +type PushNotificationConfig struct { + // Identity is our identity key + Identity *ecdsa.PrivateKey + // SendEnabled indicates whether we should be sending push notifications + SendEnabled bool + // RemoteNotificationsEnabled is whether we should register with a remote server for push notifications + RemoteNotificationsEnabled bool + + // AllowOnlyFromContacts indicates whether we should be receiving push notifications + // only from contacts + AllowOnlyFromContacts bool + // ContactIDs is the public keys for each contact that we allow notifications from + ContactIDs []*ecdsa.PublicKey + // MutedChatIDs is the IDs of the chats we don't want to receive notifications from + MutedChatIDs []string + // PushNotificationServers is an array of push notification servers we want to register with + PushNotificationServers []*PushNotificationServer + // InstallationID is the installation-id for this device + InstallationID string +} + +type PushNotificationService struct { + persistence *PushNotificationPersistence + config *PushNotificationConfig + + // lastPushNotificationRegister is the latest known push notification register message + lastPushNotificationRegister *protobuf.PushNotificationRegister + + // AccessToken is the access token that is currently being used + AccessToken string + // DeviceToken is the device token for this device + DeviceToken string + + // randomReader only used for testing so we have deterministic encryption + reader io.Reader +} + +func NewPushNotificationService(persistence *PushNotificationPersistence) *PushNotificationService { + return &PushNotificationService{persistence: persistence, reader: rand.Reader} +} + +// This likely will return a channel as it's an asynchrous operation +func fetchNotificationInfoFor(publicKey *ecdsa.PublicKey) error { + return nil +} + +// Sends an actual push notification, where do we get the chatID? +func sendPushNotificationTo(publicKey *ecdsa.PublicKey, chatID string) error { + return nil +} + +// This should schedule: +// 1) Check we have reasonably fresh push notifications info +// 2) Otherwise it should fetch them +// 3) Send a push notification to the devices in question +func (p *PushNotificationService) HandleMessageSent(publicKey *ecdsa.PublicKey, spec *encryption.ProtocolMessageSpec, messageIDs [][]byte) error { + return nil +} + +func (p *PushNotificationService) NotifyOnMessageID(messageID []byte) error { + return nil +} + +func (p *PushNotificationService) mutedChatIDsHashes() [][]byte { + var mutedChatListHashes [][]byte + + for _, chatID := range p.config.MutedChatIDs { + mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) + } + + return mutedChatListHashes +} + +func (p *PushNotificationService) reEncryptTokenPair(token []byte, pair *protobuf.PushNotificationTokenPair) (*protobuf.PushNotificationTokenPair, error) { + publicKey, err := crypto.DecompressPubkey(pair.PublicKey) + if err != nil { + return nil, err + } + return p.encryptTokenPair(publicKey, token) +} + +func (p *PushNotificationService) encryptTokenPair(publicKey *ecdsa.PublicKey, token []byte) (*protobuf.PushNotificationTokenPair, error) { + sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( + ecies.ImportECDSAPublic(publicKey), + accessTokenKeyLength, + accessTokenKeyLength, + ) + if err != nil { + return nil, err + } + encryptedToken, err := encryptAccessToken(token, sharedKey, p.reader) + if err != nil { + return nil, err + } + + return &protobuf.PushNotificationTokenPair{ + Token: encryptedToken, + PublicKey: crypto.CompressPubkey(publicKey), + }, nil +} + +func (p *PushNotificationService) allowedUserList(token []byte) ([]*protobuf.PushNotificationTokenPair, error) { + var tokenPairs []*protobuf.PushNotificationTokenPair + for _, publicKey := range p.config.ContactIDs { + tokenPair, err := p.encryptTokenPair(publicKey, token) + if err != nil { + return nil, err + } + + tokenPairs = append(tokenPairs, tokenPair) + + } + return tokenPairs, nil +} + +func (p *PushNotificationService) reEncryptAllowedUserList(token []byte, oldTokenPairs []*protobuf.PushNotificationTokenPair) ([]*protobuf.PushNotificationTokenPair, error) { + var tokenPairs []*protobuf.PushNotificationTokenPair + for _, tokenPair := range oldTokenPairs { + tokenPair, err := p.reEncryptTokenPair(token, tokenPair) + if err != nil { + return nil, err + } + + tokenPairs = append(tokenPairs, tokenPair) + + } + return tokenPairs, nil +} + +func (p *PushNotificationService) buildPushNotificationOptionsMessage(token string) (*protobuf.PushNotificationOptions, error) { + allowedUserList, err := p.allowedUserList([]byte(token)) + if err != nil { + return nil, err + } + + options := &protobuf.PushNotificationOptions{ + InstallationId: p.config.InstallationID, + Token: p.DeviceToken, + Enabled: p.config.RemoteNotificationsEnabled, + BlockedChatList: p.mutedChatIDsHashes(), + AllowedUserList: allowedUserList, + } + return options, nil +} + +func (p *PushNotificationService) buildPushNotificationRegisterMessage() (*protobuf.PushNotificationRegister, error) { + pushNotificationPreferences := &protobuf.PushNotificationPreferences{} + + if p.lastPushNotificationRegister != nil { + if err := proto.Unmarshal(p.lastPushNotificationRegister.Payload, pushNotificationPreferences); err != nil { + return nil, err + } + } + + // Increment version + pushNotificationPreferences.Version += 1 + + // Generate new token + token := uuid.New().String() + pushNotificationPreferences.AccessToken = token + + // build options for this device + ourOptions, err := p.buildPushNotificationOptionsMessage(token) + if err != nil { + return nil, err + } + + options := []*protobuf.PushNotificationOptions{ourOptions} + // Re-encrypt token for previous options that don't belong to this + // device + for _, option := range pushNotificationPreferences.Options { + if option.InstallationId != p.config.InstallationID { + newAllowedUserList, err := p.reEncryptAllowedUserList([]byte(token), option.AllowedUserList) + if err != nil { + return nil, err + } + option.AllowedUserList = newAllowedUserList + options = append(options, option) + } + } + + pushNotificationPreferences.Options = options + + // Marshal + payload, err := proto.Marshal(pushNotificationPreferences) + if err != nil { + return nil, err + } + + message := &protobuf.PushNotificationRegister{Payload: payload} + return message, nil +} + +func (p *PushNotificationService) Register(deviceToken string) error { + return nil +} + +// HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database +func (p *PushNotificationService) HandlePushNotificationRegistrationResponse(response *protobuf.PushNotificationRegistrationResponse) error { + return nil +} + +// HandlePushNotificationAdvertisement should store any info related to push notifications +func (p *PushNotificationService) HandlePushNotificationAdvertisement(info *protobuf.PushNotificationAdvertisementInfo) error { + return nil +} + +// HandlePushNotificationQueryResponse should update the data in the database for a given user +func (p *PushNotificationService) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { + return nil +} + +// HandlePushNotificationAcknowledgement should set the request as processed +func (p *PushNotificationService) HandlePushNotificationAcknowledgement(ack *protobuf.PushNotificationAcknowledgement) error { + return nil +} + +func (p *PushNotificationService) SetContactIDs(contactIDs []*ecdsa.PublicKey) error { + p.config.ContactIDs = contactIDs + // Update or schedule update + return nil +} + +func (p *PushNotificationService) SetMutedChatIDs(chatIDs []string) error { + p.config.MutedChatIDs = chatIDs + // Update or schedule update + return nil +} + +func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(reader, nonce); err != nil { + return nil, err + } + + return gcm.Seal(nonce, nonce, plaintext, nil), nil +} + +func shake256(input string) []byte { + buf := []byte(input) + h := make([]byte, 64) + sha3.ShakeSum256(h, buf) + return h +} diff --git a/protocol/push_notification_persistence.go b/protocol/push_notification_persistence.go new file mode 100644 index 000000000..b41057917 --- /dev/null +++ b/protocol/push_notification_persistence.go @@ -0,0 +1,26 @@ +package protocol + +import ( + "crypto/ecdsa" + "database/sql" +) + +type PushNotificationPersistence struct { + db *sql.DB +} + +func NewPushNotificationPersistence(db *sql.DB) *PushNotificationPersistence { + return &PushNotificationPersistence{db: db} +} + +func (p *PushNotificationPersistence) TrackPushNotification(messageID []byte) error { + return nil +} + +func (p *PushNotificationPersistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { + return false, nil +} +func (p *PushNotificationPersistence) PushNotificationSentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { + + return nil +} diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go new file mode 100644 index 000000000..955af1968 --- /dev/null +++ b/protocol/push_notification_test.go @@ -0,0 +1,219 @@ +package protocol + +import ( + "bytes" + "crypto/ecdsa" + "math/rand" + + "testing" + + "github.com/golang/protobuf/proto" + "github.com/google/uuid" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/stretchr/testify/require" +) + +func TestBuildPushNotificationRegisterMessage(t *testing.T) { + myDeviceToken := "device-token" + myInstallationID := "installationID" + mutedChatList := []string{"a", "b"} + + // build chat lish hashes + var mutedChatListHashes [][]byte + for _, chatID := range mutedChatList { + mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) + } + + identity, err := crypto.GenerateKey() + contactKey, err := crypto.GenerateKey() + require.NoError(t, err) + contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} + + // Set random generator for uuid + var seed int64 = 1 + uuid.SetRand(rand.New(rand.NewSource(seed))) + + // Get token + expectedUUID := uuid.New().String() + + // set up reader + reader := bytes.NewReader([]byte(expectedUUID)) + + sharedKey, err := ecies.ImportECDSA(identity).GenerateShared( + ecies.ImportECDSAPublic(&contactKey.PublicKey), + accessTokenKeyLength, + accessTokenKeyLength, + ) + require.NoError(t, err) + // build encrypted token + encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) + require.NoError(t, err) + + tokenPair := &protobuf.PushNotificationTokenPair{ + Token: encryptedToken, + PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), + } + + // Reset random generator + uuid.SetRand(rand.New(rand.NewSource(seed))) + + config := &PushNotificationConfig{ + Identity: identity, + RemoteNotificationsEnabled: true, + MutedChatIDs: mutedChatList, + ContactIDs: contactIDs, + InstallationID: myInstallationID, + } + + service := &PushNotificationService{} + service.config = config + service.DeviceToken = myDeviceToken + // Set reader + service.reader = bytes.NewReader([]byte(expectedUUID)) + + options := &protobuf.PushNotificationOptions{ + Token: myDeviceToken, + InstallationId: myInstallationID, + Enabled: true, + BlockedChatList: mutedChatListHashes, + AllowedUserList: []*protobuf.PushNotificationTokenPair{tokenPair}, + } + + preferences := &protobuf.PushNotificationPreferences{ + Options: []*protobuf.PushNotificationOptions{options}, + Version: 1, + AccessToken: expectedUUID, + } + + // Marshal message + marshaledPreferences, err := proto.Marshal(preferences) + require.NoError(t, err) + + expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} + actualMessage, err := service.buildPushNotificationRegisterMessage() + require.NoError(t, err) + + require.Equal(t, expectedMessage, actualMessage) +} + +func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { + deviceToken1 := "device-token-1" + deviceToken2 := "device-token-2" + installationID1 := "installationID-1" + installationID2 := "installationID-2" + + contactKey, err := crypto.GenerateKey() + require.NoError(t, err) + + // build previous push notification options + options2 := &protobuf.PushNotificationOptions{ + Token: deviceToken2, + InstallationId: installationID2, + Enabled: true, + AllowedUserList: []*protobuf.PushNotificationTokenPair{{ + Token: []byte{0x01}, + PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), + }}, + } + + preferences2 := &protobuf.PushNotificationPreferences{ + Options: []*protobuf.PushNotificationOptions{options2}, + Version: 1, + AccessToken: "some-token", + } + + // Marshal message + marshaledPreferences2, err := proto.Marshal(preferences2) + require.NoError(t, err) + + lastPushNotificationRegister := &protobuf.PushNotificationRegister{Payload: marshaledPreferences2} + + mutedChatList := []string{"a", "b"} + + // build chat lish hashes + var mutedChatListHashes [][]byte + for _, chatID := range mutedChatList { + mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) + } + + identity, err := crypto.GenerateKey() + contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} + + // Set random generator for uuid + var seed int64 = 1 + uuid.SetRand(rand.New(rand.NewSource(seed))) + + // Get token + expectedUUID := uuid.New().String() + + // set up reader + reader := bytes.NewReader([]byte(expectedUUID)) + + sharedKey, err := ecies.ImportECDSA(identity).GenerateShared( + ecies.ImportECDSAPublic(&contactKey.PublicKey), + accessTokenKeyLength, + accessTokenKeyLength, + ) + require.NoError(t, err) + // build encrypted token + encryptedToken1, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) + require.NoError(t, err) + + encryptedToken2, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) + require.NoError(t, err) + + tokenPair1 := &protobuf.PushNotificationTokenPair{ + Token: encryptedToken1, + PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), + } + + tokenPair2 := &protobuf.PushNotificationTokenPair{ + Token: encryptedToken2, + PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), + } + + // Reset random generator + uuid.SetRand(rand.New(rand.NewSource(seed))) + + config := &PushNotificationConfig{ + Identity: identity, + RemoteNotificationsEnabled: true, + MutedChatIDs: mutedChatList, + ContactIDs: contactIDs, + InstallationID: installationID1, + } + + service := &PushNotificationService{} + service.config = config + service.DeviceToken = deviceToken1 + service.lastPushNotificationRegister = lastPushNotificationRegister + // Set reader + service.reader = bytes.NewReader([]byte(expectedUUID)) + + options1 := &protobuf.PushNotificationOptions{ + Token: deviceToken1, + InstallationId: installationID1, + Enabled: true, + BlockedChatList: mutedChatListHashes, + AllowedUserList: []*protobuf.PushNotificationTokenPair{tokenPair1}, + } + options2.AllowedUserList = []*protobuf.PushNotificationTokenPair{tokenPair2} + + preferences := &protobuf.PushNotificationPreferences{ + Options: []*protobuf.PushNotificationOptions{options1, options2}, + Version: 2, + AccessToken: expectedUUID, + } + + // Marshal message + marshaledPreferences, err := proto.Marshal(preferences) + require.NoError(t, err) + + expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} + actualMessage, err := service.buildPushNotificationRegisterMessage() + require.NoError(t, err) + + require.Equal(t, expectedMessage, actualMessage) +} From 4ded7bf74c52c5c044806144d82f4763631f1805 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 30 Jun 2020 10:30:58 +0200 Subject: [PATCH 03/46] Add skeleton for server and separate namespace for client --- protocol/messenger.go | 15 +++--- .../push_notification.go | 46 +++++++++---------- .../push_notification_persistence.go | 26 +++++++++++ .../push_notification_test.go | 28 +++++------ protocol/push_notification_persistence.go | 26 ----------- .../push_notification_server.go | 32 +++++++++++++ .../push_notification_server_persistence.go | 13 ++++++ .../push_notification_server_test.go | 13 ++++++ 8 files changed, 129 insertions(+), 70 deletions(-) rename protocol/{ => push_notification_client}/push_notification.go (74%) create mode 100644 protocol/push_notification_client/push_notification_persistence.go rename protocol/{ => push_notification_client}/push_notification_test.go (90%) delete mode 100644 protocol/push_notification_persistence.go create mode 100644 protocol/push_notification_server/push_notification_server.go create mode 100644 protocol/push_notification_server/push_notification_server_persistence.go create mode 100644 protocol/push_notification_server/push_notification_server_test.go diff --git a/protocol/messenger.go b/protocol/messenger.go index 176dabbf8..a1ebe3824 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -25,6 +25,7 @@ import ( "github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/images" "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/push_notification_client" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/transport" wakutransp "github.com/status-im/status-go/protocol/transport/waku" @@ -57,7 +58,7 @@ type Messenger struct { encryptor *encryption.Protocol processor *messageProcessor handler *MessageHandler - pushNotificationService *PushNotificationService + pushNotificationClient *push_notification_client.Client logger *zap.Logger verifyTransactionClient EthClient featureFlags featureFlags @@ -329,8 +330,8 @@ func NewMessenger( logger, ) - pushNotificationPersistence := NewPushNotificationPersistence(database) - pushNotificationService := NewPushNotificationService(pushNotificationPersistence) + pushNotificationClientPersistence := push_notification_client.NewPersistence(database) + pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence) processor, err := newMessageProcessor( identity, @@ -339,7 +340,7 @@ func NewMessenger( transp, logger, c.featureFlags, - pushNotificationService.HandleMessageSent, + pushNotificationClient.HandleMessageSent, ) if err != nil { return nil, errors.Wrap(err, "failed to create messageProcessor") @@ -355,7 +356,7 @@ func NewMessenger( encryptor: encryptionProtocol, processor: processor, handler: handler, - pushNotificationService: pushNotificationService, + pushNotificationClient: pushNotificationClient, featureFlags: c.featureFlags, systemMessagesTranslations: c.systemMessagesTranslations, allChats: make(map[string]*Chat), @@ -1473,8 +1474,8 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } // If the chat is not public, we instruct the pushNotificationService to send a notification - if !chat.Public() && m.pushNotificationService != nil { - if err := m.pushNotificationService.NotifyOnMessageID(id); err != nil { + if !chat.Public() && m.pushNotificationClient != nil { + if err := m.pushNotificationClient.NotifyOnMessageID(id); err != nil { return nil, err } diff --git a/protocol/push_notification.go b/protocol/push_notification_client/push_notification.go similarity index 74% rename from protocol/push_notification.go rename to protocol/push_notification_client/push_notification.go index 5d57233cf..6416eb298 100644 --- a/protocol/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -1,4 +1,4 @@ -package protocol +package push_notification_client import ( "crypto/aes" @@ -26,7 +26,7 @@ type PushNotificationServer struct { registered bool } -type PushNotificationConfig struct { +type Config struct { // Identity is our identity key Identity *ecdsa.PrivateKey // SendEnabled indicates whether we should be sending push notifications @@ -47,9 +47,9 @@ type PushNotificationConfig struct { InstallationID string } -type PushNotificationService struct { - persistence *PushNotificationPersistence - config *PushNotificationConfig +type Client struct { + persistence *Persistence + config *Config // lastPushNotificationRegister is the latest known push notification register message lastPushNotificationRegister *protobuf.PushNotificationRegister @@ -63,8 +63,8 @@ type PushNotificationService struct { reader io.Reader } -func NewPushNotificationService(persistence *PushNotificationPersistence) *PushNotificationService { - return &PushNotificationService{persistence: persistence, reader: rand.Reader} +func New(persistence *Persistence) *Client { + return &Client{persistence: persistence, reader: rand.Reader} } // This likely will return a channel as it's an asynchrous operation @@ -81,15 +81,15 @@ func sendPushNotificationTo(publicKey *ecdsa.PublicKey, chatID string) error { // 1) Check we have reasonably fresh push notifications info // 2) Otherwise it should fetch them // 3) Send a push notification to the devices in question -func (p *PushNotificationService) HandleMessageSent(publicKey *ecdsa.PublicKey, spec *encryption.ProtocolMessageSpec, messageIDs [][]byte) error { +func (p *Client) HandleMessageSent(publicKey *ecdsa.PublicKey, spec *encryption.ProtocolMessageSpec, messageIDs [][]byte) error { return nil } -func (p *PushNotificationService) NotifyOnMessageID(messageID []byte) error { +func (p *Client) NotifyOnMessageID(messageID []byte) error { return nil } -func (p *PushNotificationService) mutedChatIDsHashes() [][]byte { +func (p *Client) mutedChatIDsHashes() [][]byte { var mutedChatListHashes [][]byte for _, chatID := range p.config.MutedChatIDs { @@ -99,7 +99,7 @@ func (p *PushNotificationService) mutedChatIDsHashes() [][]byte { return mutedChatListHashes } -func (p *PushNotificationService) reEncryptTokenPair(token []byte, pair *protobuf.PushNotificationTokenPair) (*protobuf.PushNotificationTokenPair, error) { +func (p *Client) reEncryptTokenPair(token []byte, pair *protobuf.PushNotificationTokenPair) (*protobuf.PushNotificationTokenPair, error) { publicKey, err := crypto.DecompressPubkey(pair.PublicKey) if err != nil { return nil, err @@ -107,7 +107,7 @@ func (p *PushNotificationService) reEncryptTokenPair(token []byte, pair *protobu return p.encryptTokenPair(publicKey, token) } -func (p *PushNotificationService) encryptTokenPair(publicKey *ecdsa.PublicKey, token []byte) (*protobuf.PushNotificationTokenPair, error) { +func (p *Client) encryptTokenPair(publicKey *ecdsa.PublicKey, token []byte) (*protobuf.PushNotificationTokenPair, error) { sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), accessTokenKeyLength, @@ -127,7 +127,7 @@ func (p *PushNotificationService) encryptTokenPair(publicKey *ecdsa.PublicKey, t }, nil } -func (p *PushNotificationService) allowedUserList(token []byte) ([]*protobuf.PushNotificationTokenPair, error) { +func (p *Client) allowedUserList(token []byte) ([]*protobuf.PushNotificationTokenPair, error) { var tokenPairs []*protobuf.PushNotificationTokenPair for _, publicKey := range p.config.ContactIDs { tokenPair, err := p.encryptTokenPair(publicKey, token) @@ -141,7 +141,7 @@ func (p *PushNotificationService) allowedUserList(token []byte) ([]*protobuf.Pus return tokenPairs, nil } -func (p *PushNotificationService) reEncryptAllowedUserList(token []byte, oldTokenPairs []*protobuf.PushNotificationTokenPair) ([]*protobuf.PushNotificationTokenPair, error) { +func (p *Client) reEncryptAllowedUserList(token []byte, oldTokenPairs []*protobuf.PushNotificationTokenPair) ([]*protobuf.PushNotificationTokenPair, error) { var tokenPairs []*protobuf.PushNotificationTokenPair for _, tokenPair := range oldTokenPairs { tokenPair, err := p.reEncryptTokenPair(token, tokenPair) @@ -155,7 +155,7 @@ func (p *PushNotificationService) reEncryptAllowedUserList(token []byte, oldToke return tokenPairs, nil } -func (p *PushNotificationService) buildPushNotificationOptionsMessage(token string) (*protobuf.PushNotificationOptions, error) { +func (p *Client) buildPushNotificationOptionsMessage(token string) (*protobuf.PushNotificationOptions, error) { allowedUserList, err := p.allowedUserList([]byte(token)) if err != nil { return nil, err @@ -171,7 +171,7 @@ func (p *PushNotificationService) buildPushNotificationOptionsMessage(token stri return options, nil } -func (p *PushNotificationService) buildPushNotificationRegisterMessage() (*protobuf.PushNotificationRegister, error) { +func (p *Client) buildPushNotificationRegisterMessage() (*protobuf.PushNotificationRegister, error) { pushNotificationPreferences := &protobuf.PushNotificationPreferences{} if p.lastPushNotificationRegister != nil { @@ -219,37 +219,37 @@ func (p *PushNotificationService) buildPushNotificationRegisterMessage() (*proto return message, nil } -func (p *PushNotificationService) Register(deviceToken string) error { +func (p *Client) Register(deviceToken string) error { return nil } // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database -func (p *PushNotificationService) HandlePushNotificationRegistrationResponse(response *protobuf.PushNotificationRegistrationResponse) error { +func (p *Client) HandlePushNotificationRegistrationResponse(response *protobuf.PushNotificationRegistrationResponse) error { return nil } // HandlePushNotificationAdvertisement should store any info related to push notifications -func (p *PushNotificationService) HandlePushNotificationAdvertisement(info *protobuf.PushNotificationAdvertisementInfo) error { +func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotificationAdvertisementInfo) error { return nil } // HandlePushNotificationQueryResponse should update the data in the database for a given user -func (p *PushNotificationService) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { +func (p *Client) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { return nil } // HandlePushNotificationAcknowledgement should set the request as processed -func (p *PushNotificationService) HandlePushNotificationAcknowledgement(ack *protobuf.PushNotificationAcknowledgement) error { +func (p *Client) HandlePushNotificationAcknowledgement(ack *protobuf.PushNotificationAcknowledgement) error { return nil } -func (p *PushNotificationService) SetContactIDs(contactIDs []*ecdsa.PublicKey) error { +func (p *Client) SetContactIDs(contactIDs []*ecdsa.PublicKey) error { p.config.ContactIDs = contactIDs // Update or schedule update return nil } -func (p *PushNotificationService) SetMutedChatIDs(chatIDs []string) error { +func (p *Client) SetMutedChatIDs(chatIDs []string) error { p.config.MutedChatIDs = chatIDs // Update or schedule update return nil diff --git a/protocol/push_notification_client/push_notification_persistence.go b/protocol/push_notification_client/push_notification_persistence.go new file mode 100644 index 000000000..7305ced91 --- /dev/null +++ b/protocol/push_notification_client/push_notification_persistence.go @@ -0,0 +1,26 @@ +package push_notification_client + +import ( + "crypto/ecdsa" + "database/sql" +) + +type Persistence struct { + db *sql.DB +} + +func NewPersistence(db *sql.DB) *Persistence { + return &Persistence{db: db} +} + +func (p *Persistence) TrackPushNotification(messageID []byte) error { + return nil +} + +func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { + return false, nil +} +func (p *Persistence) SentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { + + return nil +} diff --git a/protocol/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go similarity index 90% rename from protocol/push_notification_test.go rename to protocol/push_notification_client/push_notification_test.go index 955af1968..a07ded2d4 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -1,4 +1,4 @@ -package protocol +package push_notification_client import ( "bytes" @@ -59,7 +59,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - config := &PushNotificationConfig{ + config := &Config{ Identity: identity, RemoteNotificationsEnabled: true, MutedChatIDs: mutedChatList, @@ -67,11 +67,11 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { InstallationID: myInstallationID, } - service := &PushNotificationService{} - service.config = config - service.DeviceToken = myDeviceToken + client := &Client{} + client.config = config + client.DeviceToken = myDeviceToken // Set reader - service.reader = bytes.NewReader([]byte(expectedUUID)) + client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationOptions{ Token: myDeviceToken, @@ -92,7 +92,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { require.NoError(t, err) expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} - actualMessage, err := service.buildPushNotificationRegisterMessage() + actualMessage, err := client.buildPushNotificationRegisterMessage() require.NoError(t, err) require.Equal(t, expectedMessage, actualMessage) @@ -177,7 +177,7 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - config := &PushNotificationConfig{ + config := &Config{ Identity: identity, RemoteNotificationsEnabled: true, MutedChatIDs: mutedChatList, @@ -185,12 +185,12 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { InstallationID: installationID1, } - service := &PushNotificationService{} - service.config = config - service.DeviceToken = deviceToken1 - service.lastPushNotificationRegister = lastPushNotificationRegister + client := &Client{} + client.config = config + client.DeviceToken = deviceToken1 + client.lastPushNotificationRegister = lastPushNotificationRegister // Set reader - service.reader = bytes.NewReader([]byte(expectedUUID)) + client.reader = bytes.NewReader([]byte(expectedUUID)) options1 := &protobuf.PushNotificationOptions{ Token: deviceToken1, @@ -212,7 +212,7 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { require.NoError(t, err) expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} - actualMessage, err := service.buildPushNotificationRegisterMessage() + actualMessage, err := client.buildPushNotificationRegisterMessage() require.NoError(t, err) require.Equal(t, expectedMessage, actualMessage) diff --git a/protocol/push_notification_persistence.go b/protocol/push_notification_persistence.go deleted file mode 100644 index b41057917..000000000 --- a/protocol/push_notification_persistence.go +++ /dev/null @@ -1,26 +0,0 @@ -package protocol - -import ( - "crypto/ecdsa" - "database/sql" -) - -type PushNotificationPersistence struct { - db *sql.DB -} - -func NewPushNotificationPersistence(db *sql.DB) *PushNotificationPersistence { - return &PushNotificationPersistence{db: db} -} - -func (p *PushNotificationPersistence) TrackPushNotification(messageID []byte) error { - return nil -} - -func (p *PushNotificationPersistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { - return false, nil -} -func (p *PushNotificationPersistence) PushNotificationSentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { - - return nil -} diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go new file mode 100644 index 000000000..9dc92d333 --- /dev/null +++ b/protocol/push_notification_server/push_notification_server.go @@ -0,0 +1,32 @@ +package protocol + +import ( + "crypto/ecdsa" + "errors" + "github.com/status-im/status-go/protocol/protobuf" +) + +var ErrEmptyPushNotificationRegisterMessage = errors.New("empty PushNotificationRegisterMessage") + +type Config struct { + // Identity is our identity key + Identity *ecdsa.PrivateKey + // GorushUrl is the url for the gorush service + GorushURL string +} + +type Server struct { + persistence *Persistence + config *Config +} + +func New(persistence *Persistence) *Server { + return &Server{persistence: persistence} +} + +func (p *Server) ValidateRegistration(previousRegistration *protobuf.PushNotificationRegister, newRegistration *protobuf.PushNotificationRegister) error { + if newRegistration == nil { + return ErrEmptyPushNotificationRegisterMessage + } + return nil +} diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go new file mode 100644 index 000000000..25de3818b --- /dev/null +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -0,0 +1,13 @@ +package protocol + +import ( + "database/sql" +) + +type Persistence struct { + db *sql.DB +} + +func NewPersistence(db *sql.DB) *Persistence { + return &Persistence{db: db} +} diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go new file mode 100644 index 000000000..2f947e569 --- /dev/null +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -0,0 +1,13 @@ +package protocol + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPushNotificationServerValidateRegistration(t *testing.T) { + server := Server{} + require.Equal(t, ErrEmptyPushNotificationRegisterMessage, server.ValidateRegistration(nil, nil)) + +} From e19c799be2a7cfde6eed371ac6de3abcaaf387b8 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 30 Jun 2020 15:14:25 +0200 Subject: [PATCH 04/46] Remove PushNotificationRegister as we can use ApplicationMessage as a wrapper --- protocol/protobuf/push_notifications.pb.go | 176 +++++++----------- protocol/protobuf/push_notifications.proto | 6 - .../push_notification.go | 23 +-- .../push_notification_test.go | 27 +-- .../push_notification_server.go | 64 ++++++- .../push_notification_server_test.go | 31 ++- 6 files changed, 162 insertions(+), 165 deletions(-) diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index a544d35f7..89810ab6e 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -79,7 +79,7 @@ func (x PushNotificationRegistrationResponse_ErrorType) String() string { } func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{4, 0} + return fileDescriptor_200acd86044eaa5d, []int{3, 0} } type PushNotificationTokenPair struct { @@ -271,53 +271,6 @@ func (m *PushNotificationPreferences) GetAccessToken() string { return "" } -type PushNotificationRegister struct { - Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushNotificationRegister) Reset() { *m = PushNotificationRegister{} } -func (m *PushNotificationRegister) String() string { return proto.CompactTextString(m) } -func (*PushNotificationRegister) ProtoMessage() {} -func (*PushNotificationRegister) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{3} -} - -func (m *PushNotificationRegister) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationRegister.Unmarshal(m, b) -} -func (m *PushNotificationRegister) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationRegister.Marshal(b, m, deterministic) -} -func (m *PushNotificationRegister) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationRegister.Merge(m, src) -} -func (m *PushNotificationRegister) XXX_Size() int { - return xxx_messageInfo_PushNotificationRegister.Size(m) -} -func (m *PushNotificationRegister) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationRegister.DiscardUnknown(m) -} - -var xxx_messageInfo_PushNotificationRegister proto.InternalMessageInfo - -func (m *PushNotificationRegister) GetPayload() []byte { - if m != nil { - return m.Payload - } - return nil -} - -func (m *PushNotificationRegister) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - type PushNotificationRegistrationResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` @@ -332,7 +285,7 @@ func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotifi func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationRegistrationResponse) ProtoMessage() {} func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{4} + return fileDescriptor_200acd86044eaa5d, []int{3} } func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error { @@ -394,7 +347,7 @@ func (m *PushNotificationAdvertisementInfo) Reset() { *m = PushNotificat func (m *PushNotificationAdvertisementInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationAdvertisementInfo) ProtoMessage() {} func (*PushNotificationAdvertisementInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{5} + return fileDescriptor_200acd86044eaa5d, []int{4} } func (m *PushNotificationAdvertisementInfo) XXX_Unmarshal(b []byte) error { @@ -447,7 +400,7 @@ func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertiseme func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) } func (*ContactCodeAdvertisement) ProtoMessage() {} func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{6} + return fileDescriptor_200acd86044eaa5d, []int{5} } func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error { @@ -486,7 +439,7 @@ func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} } func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) } func (*PushNotificationQuery) ProtoMessage() {} func (*PushNotificationQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{7} + return fileDescriptor_200acd86044eaa5d, []int{6} } func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error { @@ -528,7 +481,7 @@ func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQuery func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryInfo) ProtoMessage() {} func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{8} + return fileDescriptor_200acd86044eaa5d, []int{7} } func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error { @@ -588,7 +541,7 @@ func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQ func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryResponse) ProtoMessage() {} func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{9} + return fileDescriptor_200acd86044eaa5d, []int{8} } func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error { @@ -628,7 +581,7 @@ func (m *PushNotification) Reset() { *m = PushNotification{} } func (m *PushNotification) String() string { return proto.CompactTextString(m) } func (*PushNotification) ProtoMessage() {} func (*PushNotification) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{10} + return fileDescriptor_200acd86044eaa5d, []int{9} } func (m *PushNotification) XXX_Unmarshal(b []byte) error { @@ -677,7 +630,7 @@ func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) } func (*PushNotificationRequest) ProtoMessage() {} func (*PushNotificationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{11} + return fileDescriptor_200acd86044eaa5d, []int{10} } func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error { @@ -737,7 +690,7 @@ func (m *PushNotificationAcknowledgement) Reset() { *m = PushNotificatio func (m *PushNotificationAcknowledgement) String() string { return proto.CompactTextString(m) } func (*PushNotificationAcknowledgement) ProtoMessage() {} func (*PushNotificationAcknowledgement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{12} + return fileDescriptor_200acd86044eaa5d, []int{11} } func (m *PushNotificationAcknowledgement) XXX_Unmarshal(b []byte) error { @@ -771,7 +724,6 @@ func init() { proto.RegisterType((*PushNotificationTokenPair)(nil), "protobuf.PushNotificationTokenPair") proto.RegisterType((*PushNotificationOptions)(nil), "protobuf.PushNotificationOptions") proto.RegisterType((*PushNotificationPreferences)(nil), "protobuf.PushNotificationPreferences") - proto.RegisterType((*PushNotificationRegister)(nil), "protobuf.PushNotificationRegister") proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement") @@ -786,60 +738,58 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 876 bytes of a gzipped FileDescriptorProto + // 846 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdf, 0x6f, 0xe3, 0x44, - 0x10, 0xc6, 0x49, 0xda, 0xc6, 0x93, 0x90, 0xa6, 0xab, 0x5e, 0xcf, 0x1c, 0x1c, 0xd7, 0x1a, 0x10, - 0xd5, 0x21, 0x45, 0xa2, 0x48, 0x70, 0x12, 0x4f, 0xa1, 0xe7, 0x1e, 0xa6, 0x8d, 0x13, 0x36, 0x29, - 0x3f, 0x9e, 0x2c, 0xc7, 0xde, 0xb4, 0xab, 0xf8, 0xbc, 0x66, 0x77, 0x7d, 0xa7, 0x3c, 0x20, 0x21, - 0xf1, 0xce, 0x7f, 0xc2, 0x0b, 0x2f, 0xfc, 0x71, 0xbc, 0x20, 0xaf, 0x7f, 0xd4, 0x75, 0xd2, 0x5e, - 0x9f, 0xe2, 0x99, 0xdd, 0x99, 0x9d, 0xf9, 0xe6, 0x9b, 0x2f, 0x60, 0xc4, 0x89, 0xb8, 0x76, 0x23, - 0x26, 0xe9, 0x82, 0xfa, 0x9e, 0xa4, 0x2c, 0x12, 0x83, 0x98, 0x33, 0xc9, 0x50, 0x5b, 0xfd, 0xcc, - 0x93, 0x85, 0x39, 0x81, 0x0f, 0x26, 0x89, 0xb8, 0x76, 0x2a, 0x97, 0x66, 0x6c, 0x49, 0xa2, 0x89, - 0x47, 0x39, 0xda, 0x87, 0x2d, 0x99, 0x1a, 0x86, 0x76, 0xa8, 0x1d, 0x77, 0x71, 0x66, 0xa0, 0xa7, - 0x00, 0x71, 0x32, 0x0f, 0xa9, 0xef, 0x2e, 0xc9, 0xca, 0x68, 0xa8, 0x23, 0x3d, 0xf3, 0x9c, 0x93, - 0x95, 0xf9, 0x67, 0x13, 0x1e, 0xd7, 0x53, 0x8e, 0x63, 0xf5, 0x3a, 0xfa, 0x01, 0x40, 0xe5, 0x70, - 0xe5, 0x2a, 0x26, 0x2a, 0x6b, 0xef, 0xe4, 0x8b, 0x41, 0x51, 0xcc, 0xe0, 0x8e, 0xb0, 0x81, 0xaa, - 0x68, 0xb6, 0x8a, 0x09, 0xd6, 0x65, 0xf1, 0x79, 0x53, 0x5c, 0x5a, 0x81, 0x5e, 0x14, 0xf7, 0x39, - 0xec, 0xd2, 0x48, 0x48, 0x2f, 0x0c, 0x55, 0x06, 0x97, 0x06, 0x46, 0x53, 0x9d, 0xf7, 0xaa, 0x6e, - 0x3b, 0x40, 0x06, 0xec, 0x90, 0xc8, 0x9b, 0x87, 0x24, 0x30, 0x5a, 0x87, 0xda, 0x71, 0x1b, 0x17, - 0x26, 0x1a, 0xc3, 0x9e, 0x17, 0x86, 0xec, 0x2d, 0x09, 0xdc, 0x44, 0x10, 0xee, 0x86, 0x54, 0x48, - 0x63, 0xeb, 0xb0, 0x79, 0xdc, 0x39, 0xf9, 0xe4, 0xee, 0x5a, 0x4b, 0xd4, 0xf0, 0x6e, 0x1e, 0x7d, - 0x29, 0x08, 0xbf, 0xa0, 0x42, 0xa2, 0xe7, 0xb0, 0x37, 0x0f, 0x99, 0xbf, 0x24, 0x81, 0xeb, 0x5f, - 0x7b, 0x32, 0x4b, 0xb8, 0x7d, 0xd8, 0x3c, 0xee, 0xe2, 0xdd, 0xfc, 0xe0, 0xf4, 0xda, 0x93, 0xe9, - 0x5d, 0xf3, 0x0c, 0xf4, 0xb2, 0x5b, 0x74, 0x00, 0xe8, 0xd2, 0x39, 0x77, 0xc6, 0x3f, 0x3b, 0xee, - 0x6c, 0x7c, 0x6e, 0x39, 0xee, 0xec, 0xd7, 0x89, 0xd5, 0x7f, 0x0f, 0xbd, 0x0f, 0xfa, 0x70, 0x92, - 0xfb, 0xfa, 0x1a, 0x42, 0xd0, 0x3b, 0xb3, 0xb1, 0xf5, 0xdd, 0x70, 0x6a, 0xe5, 0xbe, 0x86, 0xf9, - 0xaf, 0x06, 0x1f, 0xd6, 0x4b, 0x9c, 0x70, 0xb2, 0x20, 0x9c, 0x44, 0x3e, 0x11, 0xe8, 0x5b, 0xd8, - 0x61, 0x19, 0xba, 0x86, 0xa6, 0x5a, 0x3b, 0x7a, 0xe7, 0x18, 0x70, 0x11, 0x91, 0x62, 0xf7, 0x86, - 0x70, 0x41, 0x59, 0x06, 0x7e, 0x0b, 0x17, 0x26, 0xfa, 0x18, 0x20, 0x89, 0x38, 0xb9, 0xa2, 0x42, - 0x12, 0xae, 0x90, 0x6f, 0xe3, 0x8a, 0x07, 0x1d, 0x41, 0xd7, 0xf3, 0x7d, 0x22, 0x84, 0x9b, 0xcd, - 0xae, 0xa5, 0x66, 0xd3, 0xc9, 0x7c, 0xaa, 0x71, 0x13, 0x83, 0x51, 0x2f, 0x00, 0x17, 0xe1, 0x06, - 0xec, 0xc4, 0xde, 0x2a, 0x64, 0x5e, 0x90, 0x53, 0xb2, 0x30, 0xd1, 0x47, 0xa0, 0x0b, 0x7a, 0x15, - 0x79, 0x32, 0xe1, 0xa4, 0xe0, 0x64, 0xe9, 0x30, 0xff, 0x6b, 0xc0, 0xa7, 0x9b, 0x93, 0xf2, 0xfc, - 0x5b, 0xc4, 0x2c, 0x12, 0x24, 0x7d, 0x40, 0x24, 0xaa, 0x18, 0xf5, 0x40, 0x1b, 0x17, 0x26, 0x72, - 0x60, 0x8b, 0x70, 0xce, 0xb8, 0x4a, 0xde, 0x3b, 0x79, 0x71, 0x37, 0x5c, 0x9b, 0x12, 0x0f, 0xac, - 0x34, 0x56, 0x51, 0x38, 0x4b, 0x93, 0x6e, 0x11, 0x27, 0xbf, 0x25, 0x44, 0xc8, 0x82, 0xa3, 0x5d, - 0xac, 0xe7, 0x1e, 0x3b, 0x40, 0xaf, 0xa0, 0x13, 0xdf, 0x8c, 0x4b, 0xe1, 0xd4, 0x39, 0xf9, 0xec, - 0xee, 0x47, 0x2b, 0xb3, 0xc5, 0xd5, 0x48, 0xf3, 0x0f, 0x0d, 0xf4, 0xf2, 0xf1, 0x2a, 0xa3, 0x2c, - 0x8c, 0xc7, 0xb8, 0x60, 0xd4, 0x23, 0xd8, 0x1b, 0x0d, 0x2f, 0xce, 0xc6, 0x78, 0x64, 0xbd, 0x74, - 0x47, 0xd6, 0x74, 0x3a, 0x7c, 0x65, 0xf5, 0x35, 0xb4, 0x0f, 0xfd, 0x9f, 0x2c, 0x3c, 0xb5, 0xc7, - 0x8e, 0x3b, 0xb2, 0xa7, 0xa3, 0xe1, 0xec, 0xf4, 0xfb, 0x7e, 0x03, 0x3d, 0x81, 0x83, 0x4b, 0x67, - 0x7a, 0x39, 0x99, 0x8c, 0xf1, 0xcc, 0x7a, 0x59, 0xa5, 0x66, 0x33, 0xe5, 0xa2, 0xed, 0xcc, 0x2c, - 0xec, 0x0c, 0x2f, 0xb2, 0x17, 0xfa, 0x2d, 0xf3, 0x2f, 0x0d, 0x8e, 0xea, 0xf5, 0x0e, 0x83, 0x37, - 0x84, 0x4b, 0x2a, 0xc8, 0x6b, 0x12, 0x49, 0x3b, 0x5a, 0xb0, 0x9a, 0xac, 0x68, 0x35, 0x59, 0x59, - 0x63, 0x4e, 0x63, 0x8d, 0x39, 0x0f, 0xde, 0x7d, 0xf3, 0x77, 0x30, 0x4e, 0x59, 0x24, 0x3d, 0x5f, - 0x9e, 0xb2, 0x80, 0xdc, 0x2a, 0x05, 0x79, 0x70, 0xb0, 0x26, 0x9b, 0x2e, 0x8d, 0x16, 0x2c, 0xdf, - 0x93, 0x7b, 0xe4, 0x6a, 0xad, 0x27, 0xbc, 0x1f, 0xd7, 0xae, 0xa4, 0x5e, 0xf3, 0x05, 0x3c, 0xaa, - 0x87, 0xfe, 0x98, 0x10, 0xbe, 0x42, 0xcf, 0xa0, 0x73, 0x03, 0x41, 0xb6, 0x98, 0x5d, 0x0c, 0x25, - 0x06, 0xc2, 0xfc, 0x47, 0x5b, 0x97, 0x6b, 0x15, 0xaa, 0x10, 0xac, 0x43, 0xa4, 0x3d, 0x08, 0xa2, - 0xc6, 0x46, 0x79, 0xbc, 0x3d, 0x8d, 0x66, 0x7d, 0x1a, 0xcf, 0x37, 0x69, 0x64, 0x4b, 0xdd, 0xaa, - 0xcb, 0x9f, 0xf9, 0x0b, 0x3c, 0xdd, 0x58, 0x73, 0xb9, 0x74, 0xdf, 0x40, 0xab, 0x02, 0xf0, 0x3d, - 0x1a, 0x5b, 0xb6, 0x8a, 0x55, 0x80, 0xe9, 0x40, 0xbf, 0x7e, 0xe5, 0x21, 0x20, 0x3c, 0x86, 0x1d, - 0xa5, 0xc3, 0x65, 0xf3, 0xdb, 0xa9, 0x69, 0x07, 0xe6, 0xdf, 0xda, 0xfa, 0x5f, 0x17, 0xce, 0x56, - 0x12, 0x7d, 0x0d, 0xed, 0x7c, 0x3b, 0x0b, 0xc5, 0x7c, 0x72, 0x8f, 0x04, 0x94, 0x77, 0x53, 0x45, - 0x79, 0x4d, 0x84, 0xf0, 0xae, 0x0a, 0x59, 0x2a, 0xcc, 0x14, 0xe2, 0xfc, 0xf3, 0x86, 0xa9, 0x7a, - 0xee, 0xb1, 0x83, 0xac, 0x91, 0xa5, 0x9b, 0x26, 0xa2, 0x3c, 0xff, 0x97, 0x52, 0x8d, 0x2c, 0x71, - 0xee, 0x32, 0xbf, 0x84, 0x67, 0x6b, 0x1c, 0xf4, 0x97, 0x11, 0x7b, 0x1b, 0x92, 0xe0, 0x2a, 0xa3, - 0x73, 0x0f, 0x1a, 0x34, 0xc8, 0x41, 0x68, 0xd0, 0x60, 0xbe, 0xad, 0x6a, 0xfe, 0xea, 0xff, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xc3, 0x71, 0xe1, 0x4f, 0x1c, 0x08, 0x00, 0x00, + 0x10, 0xc6, 0x49, 0xda, 0x26, 0x93, 0x90, 0xa6, 0xab, 0x5e, 0xcf, 0x1c, 0x3a, 0xae, 0x35, 0x20, + 0xaa, 0x43, 0x8a, 0x44, 0x91, 0xe0, 0x24, 0x9e, 0x42, 0xcf, 0x3d, 0x4c, 0x1b, 0xdb, 0x6c, 0x52, + 0x7e, 0x3c, 0x59, 0x8e, 0xbd, 0x69, 0x57, 0xf1, 0x79, 0xcd, 0xee, 0xfa, 0x4e, 0x79, 0x40, 0x42, + 0xe2, 0x9d, 0xff, 0x84, 0x17, 0x5e, 0xf8, 0xe3, 0x78, 0x41, 0x5e, 0xff, 0xa8, 0xcf, 0x69, 0x4b, + 0x9f, 0xec, 0x19, 0xcf, 0xcc, 0x7e, 0xf3, 0xcd, 0xec, 0x67, 0xd0, 0x93, 0x54, 0x5c, 0x7b, 0x31, + 0x93, 0x74, 0x49, 0x03, 0x5f, 0x52, 0x16, 0x8b, 0x71, 0xc2, 0x99, 0x64, 0xa8, 0xab, 0x1e, 0x8b, + 0x74, 0x69, 0xb8, 0xf0, 0x81, 0x9b, 0x8a, 0x6b, 0xbb, 0x16, 0x34, 0x67, 0x2b, 0x12, 0xbb, 0x3e, + 0xe5, 0x68, 0x1f, 0xb6, 0x64, 0x66, 0xe8, 0xda, 0xa1, 0x76, 0x3c, 0xc0, 0xb9, 0x81, 0x9e, 0x02, + 0x24, 0xe9, 0x22, 0xa2, 0x81, 0xb7, 0x22, 0x6b, 0xbd, 0xa5, 0x3e, 0xf5, 0x72, 0xcf, 0x39, 0x59, + 0x1b, 0x7f, 0xb4, 0xe1, 0x71, 0xb3, 0xa4, 0x93, 0xa8, 0xd3, 0xd1, 0xf7, 0x00, 0xaa, 0x86, 0x27, + 0xd7, 0x09, 0x51, 0x55, 0x87, 0x27, 0x9f, 0x8f, 0x4b, 0x30, 0xe3, 0x3b, 0xd2, 0xc6, 0x0a, 0xd1, + 0x7c, 0x9d, 0x10, 0xdc, 0x93, 0xe5, 0xeb, 0x0d, 0xb8, 0x0c, 0x41, 0xaf, 0x04, 0xf7, 0x19, 0xec, + 0xd2, 0x58, 0x48, 0x3f, 0x8a, 0x54, 0x05, 0x8f, 0x86, 0x7a, 0x5b, 0x7d, 0x1f, 0xd6, 0xdd, 0x56, + 0x88, 0x74, 0xd8, 0x21, 0xb1, 0xbf, 0x88, 0x48, 0xa8, 0x77, 0x0e, 0xb5, 0xe3, 0x2e, 0x2e, 0x4d, + 0xe4, 0xc0, 0x9e, 0x1f, 0x45, 0xec, 0x2d, 0x09, 0xbd, 0x54, 0x10, 0xee, 0x45, 0x54, 0x48, 0x7d, + 0xeb, 0xb0, 0x7d, 0xdc, 0x3f, 0xf9, 0xf8, 0x6e, 0xac, 0x15, 0x6b, 0x78, 0xb7, 0xc8, 0xbe, 0x14, + 0x84, 0x5f, 0x50, 0x21, 0xd1, 0x73, 0xd8, 0x5b, 0x44, 0x2c, 0x58, 0x91, 0xd0, 0x0b, 0xae, 0x7d, + 0x99, 0x17, 0xdc, 0x3e, 0x6c, 0x1f, 0x0f, 0xf0, 0x6e, 0xf1, 0xe1, 0xf4, 0xda, 0x97, 0x59, 0xac, + 0x71, 0x06, 0xbd, 0xaa, 0x5b, 0x74, 0x00, 0xe8, 0xd2, 0x3e, 0xb7, 0x9d, 0x9f, 0x6c, 0x6f, 0xee, + 0x9c, 0x9b, 0xb6, 0x37, 0xff, 0xc5, 0x35, 0x47, 0xef, 0xa1, 0xf7, 0xa1, 0x37, 0x71, 0x0b, 0xdf, + 0x48, 0x43, 0x08, 0x86, 0x67, 0x16, 0x36, 0xbf, 0x9d, 0xcc, 0xcc, 0xc2, 0xd7, 0x32, 0xfe, 0xd1, + 0xe0, 0xc3, 0x26, 0x44, 0x97, 0x93, 0x25, 0xe1, 0x24, 0x0e, 0x88, 0x40, 0xdf, 0xc0, 0x0e, 0xcb, + 0xd9, 0xd5, 0x35, 0xd5, 0xda, 0xd1, 0xff, 0x8e, 0x01, 0x97, 0x19, 0x19, 0x77, 0x6f, 0x08, 0x17, + 0x94, 0xe5, 0xe4, 0x77, 0x70, 0x69, 0xa2, 0x8f, 0x00, 0xd2, 0x98, 0x93, 0x2b, 0x2a, 0x24, 0xe1, + 0x8a, 0xf9, 0x2e, 0xae, 0x79, 0xd0, 0x11, 0x0c, 0xfc, 0x20, 0x20, 0x42, 0x78, 0xf9, 0xec, 0x3a, + 0x6a, 0x36, 0xfd, 0xdc, 0xa7, 0x1a, 0x37, 0xfe, 0x6d, 0xc1, 0x27, 0x4d, 0x04, 0x58, 0xe5, 0xf3, + 0xe2, 0x5d, 0x24, 0x2c, 0x16, 0x24, 0x43, 0x21, 0x52, 0x95, 0xa8, 0x36, 0xa9, 0x8b, 0x4b, 0x13, + 0xd9, 0xb0, 0x45, 0x38, 0x67, 0x5c, 0xa1, 0x1b, 0x9e, 0xbc, 0xb8, 0xbb, 0xb5, 0xdb, 0x0a, 0x8f, + 0xcd, 0x2c, 0x57, 0xad, 0x5b, 0x5e, 0x26, 0xdb, 0x78, 0x4e, 0x7e, 0x4d, 0x89, 0x90, 0xe5, 0x3e, + 0x0d, 0x70, 0xaf, 0xf0, 0x58, 0x21, 0x7a, 0x05, 0xfd, 0xe4, 0x86, 0x5a, 0xd5, 0x53, 0xff, 0xe4, + 0xd3, 0xbb, 0x0f, 0xad, 0xcd, 0x01, 0xd7, 0x33, 0x8d, 0xdf, 0x35, 0xe8, 0x55, 0x87, 0xd7, 0xa7, + 0x6f, 0x62, 0xec, 0xe0, 0x72, 0xfa, 0x8f, 0x60, 0x6f, 0x3a, 0xb9, 0x38, 0x73, 0xf0, 0xd4, 0x7c, + 0xe9, 0x4d, 0xcd, 0xd9, 0x6c, 0xf2, 0xca, 0x1c, 0x69, 0x68, 0x1f, 0x46, 0x3f, 0x9a, 0x78, 0x66, + 0x39, 0xb6, 0x37, 0xb5, 0x66, 0xd3, 0xc9, 0xfc, 0xf4, 0xbb, 0x51, 0x0b, 0x3d, 0x81, 0x83, 0x4b, + 0x7b, 0x76, 0xe9, 0xba, 0x0e, 0x9e, 0x9b, 0x2f, 0xeb, 0x6b, 0xd4, 0xce, 0xf6, 0xc6, 0xb2, 0xe7, + 0x26, 0xb6, 0x27, 0x17, 0xf9, 0x09, 0xa3, 0x8e, 0xf1, 0xa7, 0x06, 0x47, 0x4d, 0xbc, 0x93, 0xf0, + 0x0d, 0xe1, 0x92, 0x0a, 0xf2, 0x9a, 0xc4, 0xd2, 0x8a, 0x97, 0xac, 0x21, 0x01, 0x5a, 0x43, 0x02, + 0x36, 0xa6, 0xdc, 0xda, 0x98, 0xf2, 0x83, 0xef, 0xa9, 0xf1, 0x1b, 0xe8, 0xa7, 0x2c, 0x96, 0x7e, + 0x20, 0x4f, 0x59, 0x48, 0xde, 0x81, 0x82, 0x7c, 0x38, 0xd8, 0x90, 0x38, 0x8f, 0xc6, 0x4b, 0x56, + 0xec, 0xf4, 0x3d, 0xd2, 0xb2, 0xd1, 0x13, 0xde, 0x4f, 0x1a, 0x21, 0x99, 0xd7, 0x78, 0x01, 0x8f, + 0x9a, 0xa9, 0x3f, 0xa4, 0x84, 0xaf, 0xd1, 0x33, 0xe8, 0xdf, 0x50, 0x90, 0x5f, 0xa2, 0x01, 0x86, + 0x8a, 0x03, 0x61, 0xfc, 0xad, 0x6d, 0x4a, 0xab, 0x4a, 0x55, 0x0c, 0x36, 0x29, 0xd2, 0x1e, 0x44, + 0x51, 0xeb, 0x56, 0x29, 0x7b, 0x77, 0x1a, 0xed, 0xe6, 0x34, 0x9e, 0xdf, 0xa6, 0x67, 0x1d, 0x15, + 0xd5, 0x94, 0x2a, 0xe3, 0x67, 0x78, 0x7a, 0x2b, 0xe6, 0xea, 0xd2, 0x7d, 0x0d, 0x9d, 0x1a, 0xc1, + 0xf7, 0xe8, 0x61, 0xd5, 0x2a, 0x56, 0x09, 0x86, 0x0d, 0xa3, 0x66, 0xc8, 0x43, 0x48, 0x78, 0x0c, + 0x3b, 0x4a, 0x33, 0xab, 0xe6, 0xb7, 0x33, 0xd3, 0x0a, 0x8d, 0xbf, 0xb4, 0xcd, 0xdf, 0x0c, 0xce, + 0xaf, 0x24, 0xfa, 0x0a, 0xba, 0xc5, 0xed, 0x2c, 0xd5, 0xed, 0xc9, 0x3d, 0x12, 0x50, 0xc5, 0x66, + 0x8a, 0xf2, 0x9a, 0x08, 0xe1, 0x5f, 0x91, 0xe2, 0xb7, 0x56, 0x9a, 0x19, 0xc5, 0xc5, 0xeb, 0xcd, + 0xa6, 0xf6, 0x0a, 0x8f, 0x15, 0xe6, 0x8d, 0xac, 0xbc, 0xac, 0x10, 0xe5, 0xc5, 0x1f, 0x45, 0x35, + 0xb2, 0xc2, 0x85, 0xcb, 0xf8, 0x02, 0x9e, 0x6d, 0xec, 0x60, 0xb0, 0x8a, 0xd9, 0xdb, 0x88, 0x84, + 0x57, 0xf9, 0x3a, 0x0f, 0xa1, 0x45, 0xc3, 0x82, 0x84, 0x16, 0x0d, 0x17, 0xdb, 0x0a, 0xf3, 0x97, + 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xd1, 0xea, 0x9e, 0xf4, 0xc8, 0x07, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index db0795bb9..d9824f360 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -28,12 +28,6 @@ message PushNotificationPreferences { string access_token = 4; } -message PushNotificationRegister { - bytes payload = 1; - bytes signature = 2; -} - - message PushNotificationRegistrationResponse { bool success = 1; ErrorType error = 2; diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 6416eb298..89bbc4c4c 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -9,8 +9,6 @@ import ( "golang.org/x/crypto/sha3" - "github.com/golang/protobuf/proto" - "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto" @@ -51,8 +49,8 @@ type Client struct { persistence *Persistence config *Config - // lastPushNotificationRegister is the latest known push notification register message - lastPushNotificationRegister *protobuf.PushNotificationRegister + // lastPushNotificationPreferences is the latest known push notification preferences message + lastPushNotificationPreferences *protobuf.PushNotificationPreferences // AccessToken is the access token that is currently being used AccessToken string @@ -171,13 +169,11 @@ func (p *Client) buildPushNotificationOptionsMessage(token string) (*protobuf.Pu return options, nil } -func (p *Client) buildPushNotificationRegisterMessage() (*protobuf.PushNotificationRegister, error) { +func (p *Client) buildPushNotificationPreferencesMessage() (*protobuf.PushNotificationPreferences, error) { pushNotificationPreferences := &protobuf.PushNotificationPreferences{} - if p.lastPushNotificationRegister != nil { - if err := proto.Unmarshal(p.lastPushNotificationRegister.Payload, pushNotificationPreferences); err != nil { - return nil, err - } + if p.lastPushNotificationPreferences != nil { + pushNotificationPreferences = p.lastPushNotificationPreferences } // Increment version @@ -209,14 +205,7 @@ func (p *Client) buildPushNotificationRegisterMessage() (*protobuf.PushNotificat pushNotificationPreferences.Options = options - // Marshal - payload, err := proto.Marshal(pushNotificationPreferences) - if err != nil { - return nil, err - } - - message := &protobuf.PushNotificationRegister{Payload: payload} - return message, nil + return pushNotificationPreferences, nil } func (p *Client) Register(deviceToken string) error { diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index a07ded2d4..446ec61e6 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -7,7 +7,6 @@ import ( "testing" - "github.com/golang/protobuf/proto" "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" @@ -87,15 +86,10 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { AccessToken: expectedUUID, } - // Marshal message - marshaledPreferences, err := proto.Marshal(preferences) + actualMessage, err := client.buildPushNotificationPreferencesMessage() require.NoError(t, err) - expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} - actualMessage, err := client.buildPushNotificationRegisterMessage() - require.NoError(t, err) - - require.Equal(t, expectedMessage, actualMessage) + require.Equal(t, preferences, actualMessage) } func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { @@ -124,12 +118,6 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { AccessToken: "some-token", } - // Marshal message - marshaledPreferences2, err := proto.Marshal(preferences2) - require.NoError(t, err) - - lastPushNotificationRegister := &protobuf.PushNotificationRegister{Payload: marshaledPreferences2} - mutedChatList := []string{"a", "b"} // build chat lish hashes @@ -188,7 +176,7 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { client := &Client{} client.config = config client.DeviceToken = deviceToken1 - client.lastPushNotificationRegister = lastPushNotificationRegister + client.lastPushNotificationPreferences = preferences2 // Set reader client.reader = bytes.NewReader([]byte(expectedUUID)) @@ -207,13 +195,8 @@ func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { AccessToken: expectedUUID, } - // Marshal message - marshaledPreferences, err := proto.Marshal(preferences) + actualMessage, err := client.buildPushNotificationPreferencesMessage() require.NoError(t, err) - expectedMessage := &protobuf.PushNotificationRegister{Payload: marshaledPreferences} - actualMessage, err := client.buildPushNotificationRegisterMessage() - require.NoError(t, err) - - require.Equal(t, expectedMessage, actualMessage) + require.Equal(t, preferences, actualMessage) } diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 9dc92d333..f9971a70b 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -1,12 +1,23 @@ package protocol import ( - "crypto/ecdsa" "errors" + "fmt" + + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + + "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/protobuf" ) -var ErrEmptyPushNotificationRegisterMessage = errors.New("empty PushNotificationRegisterMessage") +const encryptedPayloadKeyLength = 16 +const nonceLength = 12 + +var ErrInvalidPushNotificationRegisterVersion = errors.New("invalid version") +var ErrEmptyPushNotificationRegisterPayload = errors.New("empty payload") +var ErrEmptyPushNotificationRegisterPublicKey = errors.New("no public key") type Config struct { // Identity is our identity key @@ -24,9 +35,52 @@ func New(persistence *Persistence) *Server { return &Server{persistence: persistence} } -func (p *Server) ValidateRegistration(previousRegistration *protobuf.PushNotificationRegister, newRegistration *protobuf.PushNotificationRegister) error { - if newRegistration == nil { - return ErrEmptyPushNotificationRegisterMessage +func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationPreferences, publicKey *ecdsa.PublicKey, payload []byte) error { + if payload == nil { + return ErrEmptyPushNotificationRegisterPayload } + + if publicKey == nil { + return ErrEmptyPushNotificationRegisterPublicKey + } + + sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( + ecies.ImportECDSAPublic(publicKey), + encryptedPayloadKeyLength, + encryptedPayloadKeyLength, + ) + if err != nil { + return err + } + + decryptedPayload, err := decrypt(payload, sharedKey) + if err != nil { + return err + } + + fmt.Println(decryptedPayload) + + /*if newRegistration.Version < 1 { + return ErrInvalidPushNotificationRegisterVersion + }*/ return nil } + +func decrypt(cyphertext []byte, key []byte) ([]byte, error) { + if len(cyphertext) < nonceLength { + return nil, errors.New("invalid cyphertext length") + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := cyphertext[:nonceLength] + return gcm.Open(nil, nonce, cyphertext, nil) +} diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 2f947e569..5307da5c7 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -3,11 +3,38 @@ package protocol import ( "testing" + "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" + //nodecrypto "github.com/status-im/status-go/eth-node/crypto" + //"github.com/status-im/status-go/protocol/protobuf" ) func TestPushNotificationServerValidateRegistration(t *testing.T) { - server := Server{} - require.Equal(t, ErrEmptyPushNotificationRegisterMessage, server.ValidateRegistration(nil, nil)) + identity, err := crypto.GenerateKey() + require.NoError(t, err) + + key, err := crypto.GenerateKey() + require.NoError(t, err) + + config := &Config{ + Identity: identity, + } + + server := Server{config: config} + + // Empty payload + require.Equal(t, ErrEmptyPushNotificationRegisterPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) + + // Empty key + require.Equal(t, ErrEmptyPushNotificationRegisterPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) + + /* + // Invalid signature + signature, err := nodecrypto.SignBytes([]byte("a"), key) + + require.Equal(t, ErrInvalidPushNotificationRegisterVersion, server.ValidateRegistration(nil, &protobuf.PushNotificationRegister{ + Payload: []byte("btahtasht"), + Signature: signature, + }))*/ } From f984a041df90dc862eea3f0337c42d50d77b4a54 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 30 Jun 2020 16:55:24 +0200 Subject: [PATCH 05/46] decrypt push notification preferences --- .../push_notification_server.go | 69 ++++++++++++++----- .../push_notification_server_test.go | 50 ++++++++++---- 2 files changed, 89 insertions(+), 30 deletions(-) diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index f9971a70b..9c136b400 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -3,11 +3,14 @@ package protocol import ( "errors" "fmt" + "io" "crypto/aes" "crypto/cipher" "crypto/ecdsa" + "github.com/golang/protobuf/proto" + "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/protobuf" ) @@ -15,9 +18,11 @@ import ( const encryptedPayloadKeyLength = 16 const nonceLength = 12 -var ErrInvalidPushNotificationRegisterVersion = errors.New("invalid version") -var ErrEmptyPushNotificationRegisterPayload = errors.New("empty payload") -var ErrEmptyPushNotificationRegisterPublicKey = errors.New("no public key") +var ErrInvalidPushNotificationPreferencesVersion = errors.New("invalid version") +var ErrEmptyPushNotificationPreferencesPayload = errors.New("empty payload") +var ErrEmptyPushNotificationPreferencesPublicKey = errors.New("no public key") +var ErrCouldNotUnmarshalPushNotificationPreferences = errors.New("could not unmarshal preferences") +var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") type Config struct { // Identity is our identity key @@ -35,20 +40,24 @@ func New(persistence *Persistence) *Server { return &Server{persistence: persistence} } -func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationPreferences, publicKey *ecdsa.PublicKey, payload []byte) error { - if payload == nil { - return ErrEmptyPushNotificationRegisterPayload - } - - if publicKey == nil { - return ErrEmptyPushNotificationRegisterPublicKey - } - - sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( +func (p *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { + return ecies.ImportECDSA(p.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), encryptedPayloadKeyLength, encryptedPayloadKeyLength, ) +} + +func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationPreferences, publicKey *ecdsa.PublicKey, payload []byte) error { + if payload == nil { + return ErrEmptyPushNotificationPreferencesPayload + } + + if publicKey == nil { + return ErrEmptyPushNotificationPreferencesPublicKey + } + + sharedKey, err := p.generateSharedKey(publicKey) if err != nil { return err } @@ -58,17 +67,26 @@ func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotifica return err } + preferences := &protobuf.PushNotificationPreferences{} + + if err := proto.Unmarshal(decryptedPayload, preferences); err != nil { + return ErrCouldNotUnmarshalPushNotificationPreferences + } + + if preferences.Version < 1 { + return ErrInvalidPushNotificationPreferencesVersion + } fmt.Println(decryptedPayload) /*if newRegistration.Version < 1 { - return ErrInvalidPushNotificationRegisterVersion + return ErrInvalidPushNotificationPreferencesVersion }*/ return nil } func decrypt(cyphertext []byte, key []byte) ([]byte, error) { if len(cyphertext) < nonceLength { - return nil, errors.New("invalid cyphertext length") + return nil, ErrInvalidCiphertextLength } c, err := aes.NewCipher(key) @@ -82,5 +100,24 @@ func decrypt(cyphertext []byte, key []byte) ([]byte, error) { } nonce := cyphertext[:nonceLength] - return gcm.Open(nil, nonce, cyphertext, nil) + return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil) +} + +func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(reader, nonce); err != nil { + return nil, err + } + + return gcm.Seal(nonce, nonce, plaintext, nil), nil } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 5307da5c7..9fcadd47d 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -1,40 +1,62 @@ package protocol import ( + "crypto/rand" "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/require" - //nodecrypto "github.com/status-im/status-go/eth-node/crypto" - //"github.com/status-im/status-go/protocol/protobuf" + + "github.com/status-im/status-go/protocol/protobuf" ) func TestPushNotificationServerValidateRegistration(t *testing.T) { identity, err := crypto.GenerateKey() require.NoError(t, err) - key, err := crypto.GenerateKey() - require.NoError(t, err) - config := &Config{ Identity: identity, } server := Server{config: config} + key, err := crypto.GenerateKey() + require.NoError(t, err) + + sharedKey, err := server.generateSharedKey(&key.PublicKey) + require.NoError(t, err) + // Empty payload - require.Equal(t, ErrEmptyPushNotificationRegisterPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) + require.Equal(t, ErrEmptyPushNotificationPreferencesPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) // Empty key - require.Equal(t, ErrEmptyPushNotificationRegisterPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) + require.Equal(t, ErrEmptyPushNotificationPreferencesPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) - /* - // Invalid signature - signature, err := nodecrypto.SignBytes([]byte("a"), key) + // Invalid cyphertext length + require.Equal(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("too short"))) - require.Equal(t, ErrInvalidPushNotificationRegisterVersion, server.ValidateRegistration(nil, &protobuf.PushNotificationRegister{ - Payload: []byte("btahtasht"), - Signature: signature, - }))*/ + // Invalid cyphertext length + require.Equal(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("too short"))) + // Invalid ciphertext + require.Error(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("not too short but invalid"))) + + // Different key ciphertext + cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) + require.NoError(t, err) + require.Error(t, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Right cyphertext but non unmarshable payload + cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrCouldNotUnmarshalPushNotificationPreferences, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Version set to 0 + payload, err := proto.Marshal(&protobuf.PushNotificationPreferences{}) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrInvalidPushNotificationPreferencesVersion, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) } From c5077609ee67860a706877cb326f9967693333ff Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 1 Jul 2020 10:37:54 +0200 Subject: [PATCH 06/46] remove push notification preferences --- PUSH-NOTIFICATIONS-TODO.txt | 4 + protocol/protobuf/push_notifications.pb.go | 216 +++++++----------- protocol/protobuf/push_notifications.proto | 15 +- .../push_notification.go | 89 ++------ .../push_notification_test.go | 128 +---------- .../push_notification_server.go | 55 ++++- .../push_notification_server_test.go | 101 +++++++- 7 files changed, 248 insertions(+), 360 deletions(-) create mode 100644 PUSH-NOTIFICATIONS-TODO.txt diff --git a/PUSH-NOTIFICATIONS-TODO.txt b/PUSH-NOTIFICATIONS-TODO.txt new file mode 100644 index 000000000..2e0c858b9 --- /dev/null +++ b/PUSH-NOTIFICATIONS-TODO.txt @@ -0,0 +1,4 @@ +Specs changes: +- Use application metadata wrapper +- Encrypt of payload instead of signature + public key of server +- Removed preferencs + each device registers individually diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 89810ab6e..3d7fda805 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -79,7 +79,7 @@ func (x PushNotificationRegistrationResponse_ErrorType) String() string { } func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{3, 0} + return fileDescriptor_200acd86044eaa5d, []int{2, 0} } type PushNotificationTokenPair struct { @@ -133,9 +133,12 @@ type PushNotificationOptions struct { TokenType PushNotificationOptions_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationOptions_TokenType" json:"token_type,omitempty"` Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` - Enabled bool `protobuf:"varint,4,opt,name=enabled,proto3" json:"enabled,omitempty"` - AllowedUserList []*PushNotificationTokenPair `protobuf:"bytes,5,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` - BlockedChatList [][]byte `protobuf:"bytes,6,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` + Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -187,6 +190,13 @@ func (m *PushNotificationOptions) GetInstallationId() string { return "" } +func (m *PushNotificationOptions) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + func (m *PushNotificationOptions) GetEnabled() bool { if m != nil { return m.Enabled @@ -194,7 +204,14 @@ func (m *PushNotificationOptions) GetEnabled() bool { return false } -func (m *PushNotificationOptions) GetAllowedUserList() []*PushNotificationTokenPair { +func (m *PushNotificationOptions) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *PushNotificationOptions) GetAllowedUserList() [][]byte { if m != nil { return m.AllowedUserList } @@ -208,74 +225,17 @@ func (m *PushNotificationOptions) GetBlockedChatList() [][]byte { return nil } -type PushNotificationPreferences struct { - Options []*PushNotificationOptions `protobuf:"bytes,1,rep,name=options,proto3" json:"options,omitempty"` - Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - Unregister bool `protobuf:"varint,3,opt,name=unregister,proto3" json:"unregister,omitempty"` - AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushNotificationPreferences) Reset() { *m = PushNotificationPreferences{} } -func (m *PushNotificationPreferences) String() string { return proto.CompactTextString(m) } -func (*PushNotificationPreferences) ProtoMessage() {} -func (*PushNotificationPreferences) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{2} -} - -func (m *PushNotificationPreferences) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationPreferences.Unmarshal(m, b) -} -func (m *PushNotificationPreferences) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationPreferences.Marshal(b, m, deterministic) -} -func (m *PushNotificationPreferences) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationPreferences.Merge(m, src) -} -func (m *PushNotificationPreferences) XXX_Size() int { - return xxx_messageInfo_PushNotificationPreferences.Size(m) -} -func (m *PushNotificationPreferences) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationPreferences.DiscardUnknown(m) -} - -var xxx_messageInfo_PushNotificationPreferences proto.InternalMessageInfo - -func (m *PushNotificationPreferences) GetOptions() []*PushNotificationOptions { - if m != nil { - return m.Options - } - return nil -} - -func (m *PushNotificationPreferences) GetVersion() uint64 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *PushNotificationPreferences) GetUnregister() bool { +func (m *PushNotificationOptions) GetUnregister() bool { if m != nil { return m.Unregister } return false } -func (m *PushNotificationPreferences) GetAccessToken() string { - if m != nil { - return m.AccessToken - } - return "" -} - type PushNotificationRegistrationResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` RequestId []byte `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Preferences *PushNotificationPreferences `protobuf:"bytes,4,opt,name=preferences,proto3" json:"preferences,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -285,7 +245,7 @@ func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotifi func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationRegistrationResponse) ProtoMessage() {} func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{3} + return fileDescriptor_200acd86044eaa5d, []int{2} } func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error { @@ -327,13 +287,6 @@ func (m *PushNotificationRegistrationResponse) GetRequestId() []byte { return nil } -func (m *PushNotificationRegistrationResponse) GetPreferences() *PushNotificationPreferences { - if m != nil { - return m.Preferences - } - return nil -} - type PushNotificationAdvertisementInfo struct { PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` @@ -347,7 +300,7 @@ func (m *PushNotificationAdvertisementInfo) Reset() { *m = PushNotificat func (m *PushNotificationAdvertisementInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationAdvertisementInfo) ProtoMessage() {} func (*PushNotificationAdvertisementInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{4} + return fileDescriptor_200acd86044eaa5d, []int{3} } func (m *PushNotificationAdvertisementInfo) XXX_Unmarshal(b []byte) error { @@ -400,7 +353,7 @@ func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertiseme func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) } func (*ContactCodeAdvertisement) ProtoMessage() {} func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{5} + return fileDescriptor_200acd86044eaa5d, []int{4} } func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error { @@ -439,7 +392,7 @@ func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} } func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) } func (*PushNotificationQuery) ProtoMessage() {} func (*PushNotificationQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{6} + return fileDescriptor_200acd86044eaa5d, []int{5} } func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error { @@ -481,7 +434,7 @@ func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQuery func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryInfo) ProtoMessage() {} func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{7} + return fileDescriptor_200acd86044eaa5d, []int{6} } func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error { @@ -541,7 +494,7 @@ func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQ func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryResponse) ProtoMessage() {} func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{8} + return fileDescriptor_200acd86044eaa5d, []int{7} } func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error { @@ -581,7 +534,7 @@ func (m *PushNotification) Reset() { *m = PushNotification{} } func (m *PushNotification) String() string { return proto.CompactTextString(m) } func (*PushNotification) ProtoMessage() {} func (*PushNotification) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{9} + return fileDescriptor_200acd86044eaa5d, []int{8} } func (m *PushNotification) XXX_Unmarshal(b []byte) error { @@ -630,7 +583,7 @@ func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) } func (*PushNotificationRequest) ProtoMessage() {} func (*PushNotificationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{10} + return fileDescriptor_200acd86044eaa5d, []int{9} } func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error { @@ -690,7 +643,7 @@ func (m *PushNotificationAcknowledgement) Reset() { *m = PushNotificatio func (m *PushNotificationAcknowledgement) String() string { return proto.CompactTextString(m) } func (*PushNotificationAcknowledgement) ProtoMessage() {} func (*PushNotificationAcknowledgement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{11} + return fileDescriptor_200acd86044eaa5d, []int{10} } func (m *PushNotificationAcknowledgement) XXX_Unmarshal(b []byte) error { @@ -723,7 +676,6 @@ func init() { proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value) proto.RegisterType((*PushNotificationTokenPair)(nil), "protobuf.PushNotificationTokenPair") proto.RegisterType((*PushNotificationOptions)(nil), "protobuf.PushNotificationOptions") - proto.RegisterType((*PushNotificationPreferences)(nil), "protobuf.PushNotificationPreferences") proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement") @@ -738,58 +690,54 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 846 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdf, 0x6f, 0xe3, 0x44, - 0x10, 0xc6, 0x49, 0xda, 0x26, 0x93, 0x90, 0xa6, 0xab, 0x5e, 0xcf, 0x1c, 0x3a, 0xae, 0x35, 0x20, - 0xaa, 0x43, 0x8a, 0x44, 0x91, 0xe0, 0x24, 0x9e, 0x42, 0xcf, 0x3d, 0x4c, 0x1b, 0xdb, 0x6c, 0x52, - 0x7e, 0x3c, 0x59, 0x8e, 0xbd, 0x69, 0x57, 0xf1, 0x79, 0xcd, 0xee, 0xfa, 0x4e, 0x79, 0x40, 0x42, - 0xe2, 0x9d, 0xff, 0x84, 0x17, 0x5e, 0xf8, 0xe3, 0x78, 0x41, 0x5e, 0xff, 0xa8, 0xcf, 0x69, 0x4b, - 0x9f, 0xec, 0x19, 0xcf, 0xcc, 0x7e, 0xf3, 0xcd, 0xec, 0x67, 0xd0, 0x93, 0x54, 0x5c, 0x7b, 0x31, - 0x93, 0x74, 0x49, 0x03, 0x5f, 0x52, 0x16, 0x8b, 0x71, 0xc2, 0x99, 0x64, 0xa8, 0xab, 0x1e, 0x8b, - 0x74, 0x69, 0xb8, 0xf0, 0x81, 0x9b, 0x8a, 0x6b, 0xbb, 0x16, 0x34, 0x67, 0x2b, 0x12, 0xbb, 0x3e, - 0xe5, 0x68, 0x1f, 0xb6, 0x64, 0x66, 0xe8, 0xda, 0xa1, 0x76, 0x3c, 0xc0, 0xb9, 0x81, 0x9e, 0x02, - 0x24, 0xe9, 0x22, 0xa2, 0x81, 0xb7, 0x22, 0x6b, 0xbd, 0xa5, 0x3e, 0xf5, 0x72, 0xcf, 0x39, 0x59, - 0x1b, 0x7f, 0xb4, 0xe1, 0x71, 0xb3, 0xa4, 0x93, 0xa8, 0xd3, 0xd1, 0xf7, 0x00, 0xaa, 0x86, 0x27, - 0xd7, 0x09, 0x51, 0x55, 0x87, 0x27, 0x9f, 0x8f, 0x4b, 0x30, 0xe3, 0x3b, 0xd2, 0xc6, 0x0a, 0xd1, - 0x7c, 0x9d, 0x10, 0xdc, 0x93, 0xe5, 0xeb, 0x0d, 0xb8, 0x0c, 0x41, 0xaf, 0x04, 0xf7, 0x19, 0xec, - 0xd2, 0x58, 0x48, 0x3f, 0x8a, 0x54, 0x05, 0x8f, 0x86, 0x7a, 0x5b, 0x7d, 0x1f, 0xd6, 0xdd, 0x56, - 0x88, 0x74, 0xd8, 0x21, 0xb1, 0xbf, 0x88, 0x48, 0xa8, 0x77, 0x0e, 0xb5, 0xe3, 0x2e, 0x2e, 0x4d, - 0xe4, 0xc0, 0x9e, 0x1f, 0x45, 0xec, 0x2d, 0x09, 0xbd, 0x54, 0x10, 0xee, 0x45, 0x54, 0x48, 0x7d, - 0xeb, 0xb0, 0x7d, 0xdc, 0x3f, 0xf9, 0xf8, 0x6e, 0xac, 0x15, 0x6b, 0x78, 0xb7, 0xc8, 0xbe, 0x14, - 0x84, 0x5f, 0x50, 0x21, 0xd1, 0x73, 0xd8, 0x5b, 0x44, 0x2c, 0x58, 0x91, 0xd0, 0x0b, 0xae, 0x7d, - 0x99, 0x17, 0xdc, 0x3e, 0x6c, 0x1f, 0x0f, 0xf0, 0x6e, 0xf1, 0xe1, 0xf4, 0xda, 0x97, 0x59, 0xac, - 0x71, 0x06, 0xbd, 0xaa, 0x5b, 0x74, 0x00, 0xe8, 0xd2, 0x3e, 0xb7, 0x9d, 0x9f, 0x6c, 0x6f, 0xee, - 0x9c, 0x9b, 0xb6, 0x37, 0xff, 0xc5, 0x35, 0x47, 0xef, 0xa1, 0xf7, 0xa1, 0x37, 0x71, 0x0b, 0xdf, - 0x48, 0x43, 0x08, 0x86, 0x67, 0x16, 0x36, 0xbf, 0x9d, 0xcc, 0xcc, 0xc2, 0xd7, 0x32, 0xfe, 0xd1, - 0xe0, 0xc3, 0x26, 0x44, 0x97, 0x93, 0x25, 0xe1, 0x24, 0x0e, 0x88, 0x40, 0xdf, 0xc0, 0x0e, 0xcb, - 0xd9, 0xd5, 0x35, 0xd5, 0xda, 0xd1, 0xff, 0x8e, 0x01, 0x97, 0x19, 0x19, 0x77, 0x6f, 0x08, 0x17, - 0x94, 0xe5, 0xe4, 0x77, 0x70, 0x69, 0xa2, 0x8f, 0x00, 0xd2, 0x98, 0x93, 0x2b, 0x2a, 0x24, 0xe1, - 0x8a, 0xf9, 0x2e, 0xae, 0x79, 0xd0, 0x11, 0x0c, 0xfc, 0x20, 0x20, 0x42, 0x78, 0xf9, 0xec, 0x3a, - 0x6a, 0x36, 0xfd, 0xdc, 0xa7, 0x1a, 0x37, 0xfe, 0x6d, 0xc1, 0x27, 0x4d, 0x04, 0x58, 0xe5, 0xf3, - 0xe2, 0x5d, 0x24, 0x2c, 0x16, 0x24, 0x43, 0x21, 0x52, 0x95, 0xa8, 0x36, 0xa9, 0x8b, 0x4b, 0x13, - 0xd9, 0xb0, 0x45, 0x38, 0x67, 0x5c, 0xa1, 0x1b, 0x9e, 0xbc, 0xb8, 0xbb, 0xb5, 0xdb, 0x0a, 0x8f, - 0xcd, 0x2c, 0x57, 0xad, 0x5b, 0x5e, 0x26, 0xdb, 0x78, 0x4e, 0x7e, 0x4d, 0x89, 0x90, 0xe5, 0x3e, - 0x0d, 0x70, 0xaf, 0xf0, 0x58, 0x21, 0x7a, 0x05, 0xfd, 0xe4, 0x86, 0x5a, 0xd5, 0x53, 0xff, 0xe4, - 0xd3, 0xbb, 0x0f, 0xad, 0xcd, 0x01, 0xd7, 0x33, 0x8d, 0xdf, 0x35, 0xe8, 0x55, 0x87, 0xd7, 0xa7, - 0x6f, 0x62, 0xec, 0xe0, 0x72, 0xfa, 0x8f, 0x60, 0x6f, 0x3a, 0xb9, 0x38, 0x73, 0xf0, 0xd4, 0x7c, - 0xe9, 0x4d, 0xcd, 0xd9, 0x6c, 0xf2, 0xca, 0x1c, 0x69, 0x68, 0x1f, 0x46, 0x3f, 0x9a, 0x78, 0x66, - 0x39, 0xb6, 0x37, 0xb5, 0x66, 0xd3, 0xc9, 0xfc, 0xf4, 0xbb, 0x51, 0x0b, 0x3d, 0x81, 0x83, 0x4b, - 0x7b, 0x76, 0xe9, 0xba, 0x0e, 0x9e, 0x9b, 0x2f, 0xeb, 0x6b, 0xd4, 0xce, 0xf6, 0xc6, 0xb2, 0xe7, - 0x26, 0xb6, 0x27, 0x17, 0xf9, 0x09, 0xa3, 0x8e, 0xf1, 0xa7, 0x06, 0x47, 0x4d, 0xbc, 0x93, 0xf0, - 0x0d, 0xe1, 0x92, 0x0a, 0xf2, 0x9a, 0xc4, 0xd2, 0x8a, 0x97, 0xac, 0x21, 0x01, 0x5a, 0x43, 0x02, - 0x36, 0xa6, 0xdc, 0xda, 0x98, 0xf2, 0x83, 0xef, 0xa9, 0xf1, 0x1b, 0xe8, 0xa7, 0x2c, 0x96, 0x7e, - 0x20, 0x4f, 0x59, 0x48, 0xde, 0x81, 0x82, 0x7c, 0x38, 0xd8, 0x90, 0x38, 0x8f, 0xc6, 0x4b, 0x56, - 0xec, 0xf4, 0x3d, 0xd2, 0xb2, 0xd1, 0x13, 0xde, 0x4f, 0x1a, 0x21, 0x99, 0xd7, 0x78, 0x01, 0x8f, - 0x9a, 0xa9, 0x3f, 0xa4, 0x84, 0xaf, 0xd1, 0x33, 0xe8, 0xdf, 0x50, 0x90, 0x5f, 0xa2, 0x01, 0x86, - 0x8a, 0x03, 0x61, 0xfc, 0xad, 0x6d, 0x4a, 0xab, 0x4a, 0x55, 0x0c, 0x36, 0x29, 0xd2, 0x1e, 0x44, - 0x51, 0xeb, 0x56, 0x29, 0x7b, 0x77, 0x1a, 0xed, 0xe6, 0x34, 0x9e, 0xdf, 0xa6, 0x67, 0x1d, 0x15, - 0xd5, 0x94, 0x2a, 0xe3, 0x67, 0x78, 0x7a, 0x2b, 0xe6, 0xea, 0xd2, 0x7d, 0x0d, 0x9d, 0x1a, 0xc1, - 0xf7, 0xe8, 0x61, 0xd5, 0x2a, 0x56, 0x09, 0x86, 0x0d, 0xa3, 0x66, 0xc8, 0x43, 0x48, 0x78, 0x0c, - 0x3b, 0x4a, 0x33, 0xab, 0xe6, 0xb7, 0x33, 0xd3, 0x0a, 0x8d, 0xbf, 0xb4, 0xcd, 0xdf, 0x0c, 0xce, - 0xaf, 0x24, 0xfa, 0x0a, 0xba, 0xc5, 0xed, 0x2c, 0xd5, 0xed, 0xc9, 0x3d, 0x12, 0x50, 0xc5, 0x66, - 0x8a, 0xf2, 0x9a, 0x08, 0xe1, 0x5f, 0x91, 0xe2, 0xb7, 0x56, 0x9a, 0x19, 0xc5, 0xc5, 0xeb, 0xcd, - 0xa6, 0xf6, 0x0a, 0x8f, 0x15, 0xe6, 0x8d, 0xac, 0xbc, 0xac, 0x10, 0xe5, 0xc5, 0x1f, 0x45, 0x35, - 0xb2, 0xc2, 0x85, 0xcb, 0xf8, 0x02, 0x9e, 0x6d, 0xec, 0x60, 0xb0, 0x8a, 0xd9, 0xdb, 0x88, 0x84, - 0x57, 0xf9, 0x3a, 0x0f, 0xa1, 0x45, 0xc3, 0x82, 0x84, 0x16, 0x0d, 0x17, 0xdb, 0x0a, 0xf3, 0x97, - 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xd1, 0xea, 0x9e, 0xf4, 0xc8, 0x07, 0x00, 0x00, + // 781 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdd, 0x6e, 0xe2, 0x46, + 0x14, 0xae, 0x0d, 0x49, 0xf0, 0x09, 0x25, 0x64, 0x94, 0x1f, 0x37, 0x52, 0x1a, 0xe2, 0x56, 0x2a, + 0x6a, 0x25, 0xa4, 0xa6, 0x52, 0x9b, 0x5b, 0x4a, 0x9c, 0xd6, 0x4d, 0x30, 0x74, 0x80, 0xfe, 0x5c, + 0x59, 0xc6, 0x1e, 0xc2, 0x08, 0xc7, 0xf6, 0x7a, 0xc6, 0x89, 0xb8, 0x58, 0x69, 0x9f, 0x60, 0xdf, + 0x64, 0x6f, 0x72, 0xbb, 0x2f, 0xb7, 0xf2, 0xd8, 0x06, 0x62, 0x48, 0x94, 0x2b, 0x7c, 0xce, 0x9c, + 0xdf, 0xef, 0x9c, 0xef, 0x00, 0x6a, 0x18, 0xb3, 0xa9, 0xe5, 0x07, 0x9c, 0x4e, 0xa8, 0x63, 0x73, + 0x1a, 0xf8, 0xac, 0x15, 0x46, 0x01, 0x0f, 0x50, 0x45, 0xfc, 0x8c, 0xe3, 0x89, 0xd6, 0x87, 0x6f, + 0xfa, 0x31, 0x9b, 0x9a, 0x2b, 0x46, 0xc3, 0x60, 0x46, 0xfc, 0xbe, 0x4d, 0x23, 0x74, 0x00, 0x5b, + 0x3c, 0x11, 0x54, 0xa9, 0x21, 0x35, 0xab, 0x38, 0x15, 0xd0, 0x29, 0x40, 0x18, 0x8f, 0x3d, 0xea, + 0x58, 0x33, 0x32, 0x57, 0x65, 0xf1, 0xa4, 0xa4, 0x9a, 0x1b, 0x32, 0xd7, 0x3e, 0x97, 0xe0, 0xb8, + 0x18, 0xb2, 0x17, 0x8a, 0xec, 0xe8, 0x2f, 0x00, 0x11, 0xc3, 0xe2, 0xf3, 0x90, 0x88, 0xa8, 0xb5, + 0x8b, 0x9f, 0x5a, 0x79, 0x31, 0xad, 0x17, 0xdc, 0x5a, 0xa2, 0xa2, 0xe1, 0x3c, 0x24, 0x58, 0xe1, + 0xf9, 0xe7, 0xb2, 0xb8, 0xa4, 0x02, 0x25, 0x2f, 0xee, 0x07, 0xd8, 0xa3, 0x3e, 0xe3, 0xb6, 0xe7, + 0x89, 0x08, 0x16, 0x75, 0xd5, 0x92, 0x78, 0xaf, 0xad, 0xaa, 0x0d, 0x17, 0x9d, 0x43, 0xd5, 0x76, + 0x1c, 0xc2, 0x98, 0x95, 0x46, 0x29, 0x0b, 0xab, 0xdd, 0x54, 0x27, 0x12, 0x22, 0x15, 0x76, 0x88, + 0x6f, 0x8f, 0x3d, 0xe2, 0xaa, 0x5b, 0x0d, 0xa9, 0x59, 0xc1, 0xb9, 0x98, 0xbc, 0x3c, 0x90, 0x88, + 0xd1, 0xc0, 0x57, 0xb7, 0x1b, 0x52, 0xb3, 0x8c, 0x73, 0x11, 0xfd, 0x08, 0xfb, 0xb6, 0xe7, 0x05, + 0x8f, 0xc4, 0xb5, 0x62, 0x46, 0x22, 0xcb, 0xa3, 0x8c, 0xab, 0x3b, 0x8d, 0x52, 0xb3, 0x8a, 0xf7, + 0xb2, 0x87, 0x11, 0x23, 0xd1, 0x2d, 0x65, 0x3c, 0xb1, 0x1d, 0x7b, 0x81, 0x33, 0x23, 0xae, 0xe5, + 0x4c, 0x6d, 0x9e, 0xda, 0x56, 0x52, 0xdb, 0xec, 0xa1, 0x33, 0xb5, 0xb9, 0xb0, 0xfd, 0x16, 0x20, + 0xf6, 0x23, 0x72, 0x47, 0x19, 0x27, 0x91, 0xaa, 0x88, 0x72, 0x56, 0x34, 0xda, 0x35, 0x28, 0x0b, + 0x94, 0xd0, 0x11, 0xa0, 0x91, 0x79, 0x63, 0xf6, 0xfe, 0x35, 0xad, 0x61, 0xef, 0x46, 0x37, 0xad, + 0xe1, 0xff, 0x7d, 0xbd, 0xfe, 0x15, 0xfa, 0x1a, 0x94, 0x76, 0x3f, 0xd3, 0xd5, 0x25, 0x84, 0xa0, + 0x76, 0x6d, 0x60, 0xfd, 0xf7, 0xf6, 0x40, 0xcf, 0x74, 0xb2, 0xf6, 0x24, 0xc3, 0xf7, 0xc5, 0x31, + 0x60, 0x91, 0x24, 0xca, 0xbe, 0x59, 0x18, 0xf8, 0x8c, 0x24, 0x10, 0xb0, 0x58, 0x80, 0x25, 0xe6, + 0x58, 0xc1, 0xb9, 0x88, 0x4c, 0xd8, 0x22, 0x51, 0x14, 0x44, 0x62, 0x30, 0xb5, 0x8b, 0xcb, 0x97, + 0xe7, 0xbb, 0x29, 0x70, 0x4b, 0x4f, 0x7c, 0xc5, 0xb0, 0xd3, 0x30, 0xc9, 0xbe, 0x45, 0xe4, 0x5d, + 0x4c, 0x18, 0xcf, 0xa7, 0x59, 0xc5, 0x4a, 0xa6, 0x31, 0x5c, 0xed, 0x83, 0x04, 0xca, 0xc2, 0x67, + 0xb5, 0x75, 0x1d, 0xe3, 0x1e, 0xce, 0x5b, 0x3f, 0x84, 0xfd, 0x6e, 0xfb, 0xf6, 0xba, 0x87, 0xbb, + 0xfa, 0x95, 0xd5, 0xd5, 0x07, 0x83, 0xf6, 0x1f, 0x7a, 0x5d, 0x42, 0x07, 0x50, 0xff, 0x47, 0xc7, + 0x03, 0xa3, 0x67, 0x5a, 0x5d, 0x63, 0xd0, 0x6d, 0x0f, 0x3b, 0x7f, 0xd6, 0x65, 0x74, 0x02, 0x47, + 0x23, 0x73, 0x30, 0xea, 0xf7, 0x7b, 0x78, 0xa8, 0x5f, 0xad, 0x62, 0x58, 0x4a, 0x40, 0x33, 0xcc, + 0xa1, 0x8e, 0xcd, 0xf6, 0x6d, 0x9a, 0xa1, 0x5e, 0xd6, 0x3e, 0x4a, 0x70, 0x5e, 0xec, 0xad, 0xed, + 0x3e, 0x90, 0x88, 0x53, 0x46, 0xee, 0x89, 0xcf, 0x0d, 0x7f, 0x12, 0x14, 0x78, 0x23, 0x15, 0x78, + 0xb3, 0xb6, 0x90, 0xf2, 0xfa, 0x42, 0xbe, 0x75, 0xb9, 0xb5, 0xf7, 0xa0, 0x76, 0x02, 0x9f, 0xdb, + 0x0e, 0xef, 0x04, 0x2e, 0x79, 0x56, 0x0a, 0xb2, 0xe1, 0x68, 0xed, 0x2e, 0x58, 0xd4, 0x9f, 0x04, + 0xaa, 0xd4, 0x28, 0x35, 0x77, 0x5f, 0xe3, 0xe3, 0x5a, 0x4f, 0xf8, 0x20, 0x2c, 0x98, 0x24, 0x5a, + 0xed, 0x12, 0x0e, 0x8b, 0xae, 0x7f, 0xc7, 0x24, 0x9a, 0xa3, 0x33, 0xd8, 0x5d, 0x42, 0xc0, 0x44, + 0xc2, 0x2a, 0x86, 0x05, 0x06, 0x4c, 0x7b, 0x92, 0xd6, 0xef, 0x91, 0x70, 0x15, 0x08, 0x16, 0x21, + 0x92, 0xde, 0x04, 0x91, 0xbc, 0x91, 0xff, 0xcf, 0xa7, 0x51, 0x2a, 0x4e, 0x63, 0x23, 0x8f, 0xcb, + 0xc2, 0xaa, 0xc8, 0x63, 0xed, 0x3f, 0x38, 0xdd, 0x58, 0xf3, 0x82, 0x2b, 0xbf, 0x41, 0x79, 0x05, + 0xe0, 0xef, 0x5e, 0x06, 0x78, 0xd1, 0x2a, 0x16, 0x0e, 0x9a, 0x09, 0xf5, 0xa2, 0xc9, 0x5b, 0x40, + 0x38, 0x86, 0x1d, 0x71, 0x50, 0x16, 0xcd, 0x6f, 0x27, 0xa2, 0xe1, 0x6a, 0x9f, 0xa4, 0xf5, 0xdb, + 0x8c, 0x53, 0x26, 0xa1, 0x5f, 0xa1, 0x92, 0x91, 0x8a, 0x65, 0x85, 0x9e, 0xbc, 0xc2, 0xdc, 0x85, + 0x6d, 0x72, 0x08, 0xee, 0x09, 0x63, 0xf6, 0x1d, 0xc9, 0xfe, 0x0b, 0x72, 0x31, 0x81, 0x38, 0xfb, + 0x5c, 0x6e, 0xaa, 0x92, 0x69, 0xf2, 0x0b, 0x3c, 0xb3, 0x92, 0x40, 0x34, 0x22, 0xee, 0xf2, 0x02, + 0xcf, 0x70, 0xa6, 0xd2, 0x7e, 0x86, 0xb3, 0xb5, 0x1d, 0x74, 0x66, 0x7e, 0xf0, 0xe8, 0x11, 0xf7, + 0x2e, 0x5d, 0xe7, 0x1a, 0xc8, 0xd4, 0xcd, 0x40, 0x90, 0xa9, 0x3b, 0xde, 0x16, 0x35, 0xff, 0xf2, + 0x25, 0x00, 0x00, 0xff, 0xff, 0x01, 0x0b, 0xdd, 0xd4, 0xfd, 0x06, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index d9824f360..d552c11b3 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -16,23 +16,18 @@ message PushNotificationOptions { TokenType token_type = 1; string token = 2; string installation_id = 3; - bool enabled = 4; - repeated PushNotificationTokenPair allowed_user_list = 5; - repeated bytes blocked_chat_list = 6; -} - -message PushNotificationPreferences { - repeated PushNotificationOptions options = 1; - uint64 version = 2; - bool unregister = 3; string access_token = 4; + bool enabled = 5; + uint64 version = 6; + repeated bytes allowed_user_list = 7; + repeated bytes blocked_chat_list = 8; + bool unregister = 9; } message PushNotificationRegistrationResponse { bool success = 1; ErrorType error = 2; bytes request_id = 3; - PushNotificationPreferences preferences = 4; enum ErrorType { UNKNOWN_ERROR_TYPE = 0; diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 89bbc4c4c..eb7354ddd 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -11,7 +11,6 @@ import ( "github.com/google/uuid" - "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/protobuf" @@ -49,8 +48,8 @@ type Client struct { persistence *Persistence config *Config - // lastPushNotificationPreferences is the latest known push notification preferences message - lastPushNotificationPreferences *protobuf.PushNotificationPreferences + // lastPushNotificationVersion is the latest known push notification version + lastPushNotificationVersion uint64 // AccessToken is the access token that is currently being used AccessToken string @@ -97,15 +96,7 @@ func (p *Client) mutedChatIDsHashes() [][]byte { return mutedChatListHashes } -func (p *Client) reEncryptTokenPair(token []byte, pair *protobuf.PushNotificationTokenPair) (*protobuf.PushNotificationTokenPair, error) { - publicKey, err := crypto.DecompressPubkey(pair.PublicKey) - if err != nil { - return nil, err - } - return p.encryptTokenPair(publicKey, token) -} - -func (p *Client) encryptTokenPair(publicKey *ecdsa.PublicKey, token []byte) (*protobuf.PushNotificationTokenPair, error) { +func (p *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), accessTokenKeyLength, @@ -118,48 +109,33 @@ func (p *Client) encryptTokenPair(publicKey *ecdsa.PublicKey, token []byte) (*pr if err != nil { return nil, err } - - return &protobuf.PushNotificationTokenPair{ - Token: encryptedToken, - PublicKey: crypto.CompressPubkey(publicKey), - }, nil + return encryptedToken, nil } -func (p *Client) allowedUserList(token []byte) ([]*protobuf.PushNotificationTokenPair, error) { - var tokenPairs []*protobuf.PushNotificationTokenPair +func (p *Client) allowedUserList(token []byte) ([][]byte, error) { + var encryptedTokens [][]byte for _, publicKey := range p.config.ContactIDs { - tokenPair, err := p.encryptTokenPair(publicKey, token) + encryptedToken, err := p.encryptToken(publicKey, token) if err != nil { return nil, err } - tokenPairs = append(tokenPairs, tokenPair) + encryptedTokens = append(encryptedTokens, encryptedToken) } - return tokenPairs, nil + return encryptedTokens, nil } -func (p *Client) reEncryptAllowedUserList(token []byte, oldTokenPairs []*protobuf.PushNotificationTokenPair) ([]*protobuf.PushNotificationTokenPair, error) { - var tokenPairs []*protobuf.PushNotificationTokenPair - for _, tokenPair := range oldTokenPairs { - tokenPair, err := p.reEncryptTokenPair(token, tokenPair) - if err != nil { - return nil, err - } - - tokenPairs = append(tokenPairs, tokenPair) - - } - return tokenPairs, nil -} - -func (p *Client) buildPushNotificationOptionsMessage(token string) (*protobuf.PushNotificationOptions, error) { +func (p *Client) buildPushNotificationOptionsMessage() (*protobuf.PushNotificationOptions, error) { + token := uuid.New().String() allowedUserList, err := p.allowedUserList([]byte(token)) if err != nil { return nil, err } options := &protobuf.PushNotificationOptions{ + AccessToken: token, + Version: p.lastPushNotificationVersion + 1, InstallationId: p.config.InstallationID, Token: p.DeviceToken, Enabled: p.config.RemoteNotificationsEnabled, @@ -169,45 +145,6 @@ func (p *Client) buildPushNotificationOptionsMessage(token string) (*protobuf.Pu return options, nil } -func (p *Client) buildPushNotificationPreferencesMessage() (*protobuf.PushNotificationPreferences, error) { - pushNotificationPreferences := &protobuf.PushNotificationPreferences{} - - if p.lastPushNotificationPreferences != nil { - pushNotificationPreferences = p.lastPushNotificationPreferences - } - - // Increment version - pushNotificationPreferences.Version += 1 - - // Generate new token - token := uuid.New().String() - pushNotificationPreferences.AccessToken = token - - // build options for this device - ourOptions, err := p.buildPushNotificationOptionsMessage(token) - if err != nil { - return nil, err - } - - options := []*protobuf.PushNotificationOptions{ourOptions} - // Re-encrypt token for previous options that don't belong to this - // device - for _, option := range pushNotificationPreferences.Options { - if option.InstallationId != p.config.InstallationID { - newAllowedUserList, err := p.reEncryptAllowedUserList([]byte(token), option.AllowedUserList) - if err != nil { - return nil, err - } - option.AllowedUserList = newAllowedUserList - options = append(options, option) - } - } - - pushNotificationPreferences.Options = options - - return pushNotificationPreferences, nil -} - func (p *Client) Register(deviceToken string) error { return nil } diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index 446ec61e6..6ae4a1b29 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -50,11 +50,6 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) require.NoError(t, err) - tokenPair := &protobuf.PushNotificationTokenPair{ - Token: encryptedToken, - PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), - } - // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) @@ -73,130 +68,17 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationOptions{ + Version: 1, + AccessToken: expectedUUID, Token: myDeviceToken, InstallationId: myInstallationID, Enabled: true, BlockedChatList: mutedChatListHashes, - AllowedUserList: []*protobuf.PushNotificationTokenPair{tokenPair}, + AllowedUserList: [][]byte{encryptedToken}, } - preferences := &protobuf.PushNotificationPreferences{ - Options: []*protobuf.PushNotificationOptions{options}, - Version: 1, - AccessToken: expectedUUID, - } - - actualMessage, err := client.buildPushNotificationPreferencesMessage() + actualMessage, err := client.buildPushNotificationOptionsMessage() require.NoError(t, err) - require.Equal(t, preferences, actualMessage) -} - -func TestBuildPushNotificationRegisterMessageWithPrevious(t *testing.T) { - deviceToken1 := "device-token-1" - deviceToken2 := "device-token-2" - installationID1 := "installationID-1" - installationID2 := "installationID-2" - - contactKey, err := crypto.GenerateKey() - require.NoError(t, err) - - // build previous push notification options - options2 := &protobuf.PushNotificationOptions{ - Token: deviceToken2, - InstallationId: installationID2, - Enabled: true, - AllowedUserList: []*protobuf.PushNotificationTokenPair{{ - Token: []byte{0x01}, - PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), - }}, - } - - preferences2 := &protobuf.PushNotificationPreferences{ - Options: []*protobuf.PushNotificationOptions{options2}, - Version: 1, - AccessToken: "some-token", - } - - mutedChatList := []string{"a", "b"} - - // build chat lish hashes - var mutedChatListHashes [][]byte - for _, chatID := range mutedChatList { - mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) - } - - identity, err := crypto.GenerateKey() - contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} - - // Set random generator for uuid - var seed int64 = 1 - uuid.SetRand(rand.New(rand.NewSource(seed))) - - // Get token - expectedUUID := uuid.New().String() - - // set up reader - reader := bytes.NewReader([]byte(expectedUUID)) - - sharedKey, err := ecies.ImportECDSA(identity).GenerateShared( - ecies.ImportECDSAPublic(&contactKey.PublicKey), - accessTokenKeyLength, - accessTokenKeyLength, - ) - require.NoError(t, err) - // build encrypted token - encryptedToken1, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) - require.NoError(t, err) - - encryptedToken2, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) - require.NoError(t, err) - - tokenPair1 := &protobuf.PushNotificationTokenPair{ - Token: encryptedToken1, - PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), - } - - tokenPair2 := &protobuf.PushNotificationTokenPair{ - Token: encryptedToken2, - PublicKey: crypto.CompressPubkey(&contactKey.PublicKey), - } - - // Reset random generator - uuid.SetRand(rand.New(rand.NewSource(seed))) - - config := &Config{ - Identity: identity, - RemoteNotificationsEnabled: true, - MutedChatIDs: mutedChatList, - ContactIDs: contactIDs, - InstallationID: installationID1, - } - - client := &Client{} - client.config = config - client.DeviceToken = deviceToken1 - client.lastPushNotificationPreferences = preferences2 - // Set reader - client.reader = bytes.NewReader([]byte(expectedUUID)) - - options1 := &protobuf.PushNotificationOptions{ - Token: deviceToken1, - InstallationId: installationID1, - Enabled: true, - BlockedChatList: mutedChatListHashes, - AllowedUserList: []*protobuf.PushNotificationTokenPair{tokenPair1}, - } - options2.AllowedUserList = []*protobuf.PushNotificationTokenPair{tokenPair2} - - preferences := &protobuf.PushNotificationPreferences{ - Options: []*protobuf.PushNotificationOptions{options1, options2}, - Version: 2, - AccessToken: expectedUUID, - } - - actualMessage, err := client.buildPushNotificationPreferencesMessage() - require.NoError(t, err) - - require.Equal(t, preferences, actualMessage) + require.Equal(t, options, actualMessage) } diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 9c136b400..54c87ea6f 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -10,6 +10,7 @@ import ( "crypto/ecdsa" "github.com/golang/protobuf/proto" + "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/protobuf" @@ -18,11 +19,14 @@ import ( const encryptedPayloadKeyLength = 16 const nonceLength = 12 -var ErrInvalidPushNotificationPreferencesVersion = errors.New("invalid version") -var ErrEmptyPushNotificationPreferencesPayload = errors.New("empty payload") -var ErrEmptyPushNotificationPreferencesPublicKey = errors.New("no public key") -var ErrCouldNotUnmarshalPushNotificationPreferences = errors.New("could not unmarshal preferences") +var ErrInvalidPushNotificationOptionsVersion = errors.New("invalid version") +var ErrEmptyPushNotificationOptionsPayload = errors.New("empty payload") +var ErrMalformedPushNotificationOptionsInstallationID = errors.New("invalid installationID") +var ErrEmptyPushNotificationOptionsPublicKey = errors.New("no public key") +var ErrCouldNotUnmarshalPushNotificationOptions = errors.New("could not unmarshal preferences") var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") +var ErrMalformedPushNotificationOptionsAccessToken = errors.New("invalid access token") +var ErrMalformedPushNotificationOptionsDeviceToken = errors.New("invalid device token") type Config struct { // Identity is our identity key @@ -48,13 +52,21 @@ func (p *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { ) } -func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationPreferences, publicKey *ecdsa.PublicKey, payload []byte) error { +func (p *Server) validateUUID(u string) error { + if len(u) == 0 { + return errors.New("empty uuid") + } + _, err := uuid.Parse(u) + return err +} + +func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationOptions, publicKey *ecdsa.PublicKey, payload []byte) error { if payload == nil { - return ErrEmptyPushNotificationPreferencesPayload + return ErrEmptyPushNotificationOptionsPayload } if publicKey == nil { - return ErrEmptyPushNotificationPreferencesPublicKey + return ErrEmptyPushNotificationOptionsPublicKey } sharedKey, err := p.generateSharedKey(publicKey) @@ -67,19 +79,40 @@ func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotifica return err } - preferences := &protobuf.PushNotificationPreferences{} + preferences := &protobuf.PushNotificationOptions{} if err := proto.Unmarshal(decryptedPayload, preferences); err != nil { - return ErrCouldNotUnmarshalPushNotificationPreferences + return ErrCouldNotUnmarshalPushNotificationOptions } if preferences.Version < 1 { - return ErrInvalidPushNotificationPreferencesVersion + return ErrInvalidPushNotificationOptionsVersion + } + + if previousPreferences != nil && preferences.Version <= previousPreferences.Version { + return ErrInvalidPushNotificationOptionsVersion + } + + if err := p.validateUUID(preferences.InstallationId); err != nil { + return ErrMalformedPushNotificationOptionsInstallationID + } + + // Unregistering message + if preferences.Unregister { + return nil + } + + if err := p.validateUUID(preferences.AccessToken); err != nil { + return ErrMalformedPushNotificationOptionsAccessToken + } + + if len(preferences.Token) == 0 { + return ErrMalformedPushNotificationOptionsDeviceToken } fmt.Println(decryptedPayload) /*if newRegistration.Version < 1 { - return ErrInvalidPushNotificationPreferencesVersion + return ErrInvalidPushNotificationOptionsVersion }*/ return nil } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 9fcadd47d..fa3ff20f9 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -12,6 +12,8 @@ import ( ) func TestPushNotificationServerValidateRegistration(t *testing.T) { + accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" + installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" identity, err := crypto.GenerateKey() require.NoError(t, err) @@ -28,10 +30,10 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { require.NoError(t, err) // Empty payload - require.Equal(t, ErrEmptyPushNotificationPreferencesPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) + require.Equal(t, ErrEmptyPushNotificationOptionsPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) // Empty key - require.Equal(t, ErrEmptyPushNotificationPreferencesPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) + require.Equal(t, ErrEmptyPushNotificationOptionsPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) // Invalid cyphertext length require.Equal(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("too short"))) @@ -50,13 +52,100 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { // Right cyphertext but non unmarshable payload cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrCouldNotUnmarshalPushNotificationPreferences, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + require.Equal(t, ErrCouldNotUnmarshalPushNotificationOptions, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) - // Version set to 0 - payload, err := proto.Marshal(&protobuf.PushNotificationPreferences{}) + // Missing installationID + payload, err := proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + Version: 1, + }) require.NoError(t, err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrInvalidPushNotificationPreferencesVersion, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Malformed installationID + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + InstallationId: "abc", + Version: 1, + }) + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Version set to 0 + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + InstallationId: installationID, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrInvalidPushNotificationOptionsVersion, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Version lower than previous one + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrInvalidPushNotificationOptionsVersion, server.ValidateRegistration(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 2}, &key.PublicKey, cyphertext)) + + // Unregistering message + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + InstallationId: installationID, + Unregister: true, + Version: 1, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Nil(t, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Missing access token + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + InstallationId: installationID, + Version: 1, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Invalid access token + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: "bc", + InstallationId: installationID, + Version: 1, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + + // Missing device token + payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + }) + require.NoError(t, err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + require.NoError(t, err) + require.Equal(t, ErrMalformedPushNotificationOptionsDeviceToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + } From eed0df3420bdc57b4c7300cbab2c2d897e4f965e Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 1 Jul 2020 10:53:05 +0200 Subject: [PATCH 07/46] Return decrypted options on validation --- .../push_notification_server.go | 47 +++++++++---------- .../push_notification_server_test.go | 47 ++++++++++++------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 54c87ea6f..767f0a95e 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -60,61 +60,60 @@ func (p *Server) validateUUID(u string) error { return err } -func (p *Server) ValidateRegistration(previousPreferences *protobuf.PushNotificationOptions, publicKey *ecdsa.PublicKey, payload []byte) error { +// ValidateRegistration validates a new message against the last one received for a given installationID and and public key +// and return the decrypted message +func (p *Server) ValidateRegistration(previousOptions *protobuf.PushNotificationOptions, publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationOptions, error) { if payload == nil { - return ErrEmptyPushNotificationOptionsPayload + return nil, ErrEmptyPushNotificationOptionsPayload } if publicKey == nil { - return ErrEmptyPushNotificationOptionsPublicKey + return nil, ErrEmptyPushNotificationOptionsPublicKey } sharedKey, err := p.generateSharedKey(publicKey) if err != nil { - return err + return nil, err } decryptedPayload, err := decrypt(payload, sharedKey) if err != nil { - return err + return nil, err } - preferences := &protobuf.PushNotificationOptions{} + options := &protobuf.PushNotificationOptions{} - if err := proto.Unmarshal(decryptedPayload, preferences); err != nil { - return ErrCouldNotUnmarshalPushNotificationOptions + if err := proto.Unmarshal(decryptedPayload, options); err != nil { + return nil, ErrCouldNotUnmarshalPushNotificationOptions } - if preferences.Version < 1 { - return ErrInvalidPushNotificationOptionsVersion + if options.Version < 1 { + return nil, ErrInvalidPushNotificationOptionsVersion } - if previousPreferences != nil && preferences.Version <= previousPreferences.Version { - return ErrInvalidPushNotificationOptionsVersion + if previousOptions != nil && options.Version <= previousOptions.Version { + return nil, ErrInvalidPushNotificationOptionsVersion } - if err := p.validateUUID(preferences.InstallationId); err != nil { - return ErrMalformedPushNotificationOptionsInstallationID + if err := p.validateUUID(options.InstallationId); err != nil { + return nil, ErrMalformedPushNotificationOptionsInstallationID } // Unregistering message - if preferences.Unregister { - return nil + if options.Unregister { + return options, nil } - if err := p.validateUUID(preferences.AccessToken); err != nil { - return ErrMalformedPushNotificationOptionsAccessToken + if err := p.validateUUID(options.AccessToken); err != nil { + return nil, ErrMalformedPushNotificationOptionsAccessToken } - if len(preferences.Token) == 0 { - return ErrMalformedPushNotificationOptionsDeviceToken + if len(options.Token) == 0 { + return nil, ErrMalformedPushNotificationOptionsDeviceToken } fmt.Println(decryptedPayload) - /*if newRegistration.Version < 1 { - return ErrInvalidPushNotificationOptionsVersion - }*/ - return nil + return options, nil } func decrypt(cyphertext []byte, key []byte) ([]byte, error) { diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index fa3ff20f9..023cabdcd 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -30,29 +30,36 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { require.NoError(t, err) // Empty payload - require.Equal(t, ErrEmptyPushNotificationOptionsPayload, server.ValidateRegistration(nil, &key.PublicKey, nil)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, nil) + require.Equal(t, ErrEmptyPushNotificationOptionsPayload, err) // Empty key - require.Equal(t, ErrEmptyPushNotificationOptionsPublicKey, server.ValidateRegistration(nil, nil, []byte("payload"))) + _, err = server.ValidateRegistration(nil, nil, []byte("payload")) + require.Equal(t, ErrEmptyPushNotificationOptionsPublicKey, err) // Invalid cyphertext length - require.Equal(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("too short"))) + _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("too short")) + require.Equal(t, ErrInvalidCiphertextLength, err) // Invalid cyphertext length - require.Equal(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("too short"))) + _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("too short")) + require.Equal(t, ErrInvalidCiphertextLength, err) // Invalid ciphertext - require.Error(t, ErrInvalidCiphertextLength, server.ValidateRegistration(nil, &key.PublicKey, []byte("not too short but invalid"))) + _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("not too short but invalid")) + require.Error(t, ErrInvalidCiphertextLength, err) // Different key ciphertext cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) require.NoError(t, err) - require.Error(t, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Error(t, err) // Right cyphertext but non unmarshable payload cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrCouldNotUnmarshalPushNotificationOptions, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrCouldNotUnmarshalPushNotificationOptions, err) // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationOptions{ @@ -63,7 +70,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) // Malformed installationID payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -73,7 +81,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { }) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -84,7 +93,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrInvalidPushNotificationOptionsVersion, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -96,10 +106,11 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrInvalidPushNotificationOptionsVersion, server.ValidateRegistration(&protobuf.PushNotificationOptions{ + _, err = server.ValidateRegistration(&protobuf.PushNotificationOptions{ AccessToken: accessToken, InstallationId: installationID, - Version: 2}, &key.PublicKey, cyphertext)) + Version: 2}, &key.PublicKey, cyphertext) + require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -111,7 +122,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Nil(t, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Nil(t, err) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -122,7 +134,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -134,7 +147,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -146,6 +160,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - require.Equal(t, ErrMalformedPushNotificationOptionsDeviceToken, server.ValidateRegistration(nil, &key.PublicKey, cyphertext)) + _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + require.Equal(t, ErrMalformedPushNotificationOptionsDeviceToken, err) } From 24df11bcf9f887b6fd3718b97843a591028d30b7 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 1 Jul 2020 12:09:40 +0200 Subject: [PATCH 08/46] Pull push notification options from persistence --- .../push_notification_server.go | 48 +++++++++++------ .../push_notification_server_persistence.go | 17 ++++-- .../push_notification_server_test.go | 52 ++++++++++++------- 3 files changed, 80 insertions(+), 37 deletions(-) diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 767f0a95e..3cc6c7ca6 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -2,7 +2,6 @@ package protocol import ( "errors" - "fmt" "io" "crypto/aes" @@ -36,12 +35,12 @@ type Config struct { } type Server struct { - persistence *Persistence + persistence Persistence config *Config } -func New(persistence *Persistence) *Server { - return &Server{persistence: persistence} +func New(config *Config, persistence Persistence) *Server { + return &Server{persistence: persistence, config: config} } func (p *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { @@ -60,9 +59,18 @@ func (p *Server) validateUUID(u string) error { return err } +func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { + sharedKey, err := p.generateSharedKey(publicKey) + if err != nil { + return nil, err + } + + return decrypt(payload, sharedKey) +} + // ValidateRegistration validates a new message against the last one received for a given installationID and and public key // and return the decrypted message -func (p *Server) ValidateRegistration(previousOptions *protobuf.PushNotificationOptions, publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationOptions, error) { +func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationOptions, error) { if payload == nil { return nil, ErrEmptyPushNotificationOptionsPayload } @@ -71,12 +79,7 @@ func (p *Server) ValidateRegistration(previousOptions *protobuf.PushNotification return nil, ErrEmptyPushNotificationOptionsPublicKey } - sharedKey, err := p.generateSharedKey(publicKey) - if err != nil { - return nil, err - } - - decryptedPayload, err := decrypt(payload, sharedKey) + decryptedPayload, err := p.decryptRegistration(publicKey, payload) if err != nil { return nil, err } @@ -91,14 +94,19 @@ func (p *Server) ValidateRegistration(previousOptions *protobuf.PushNotification return nil, ErrInvalidPushNotificationOptionsVersion } - if previousOptions != nil && options.Version <= previousOptions.Version { - return nil, ErrInvalidPushNotificationOptionsVersion - } - if err := p.validateUUID(options.InstallationId); err != nil { return nil, ErrMalformedPushNotificationOptionsInstallationID } + previousOptions, err := p.persistence.GetPushNotificationOptions(publicKey, options.InstallationId) + if err != nil { + return nil, err + } + + if previousOptions != nil && options.Version <= previousOptions.Version { + return nil, ErrInvalidPushNotificationOptionsVersion + } + // Unregistering message if options.Unregister { return options, nil @@ -111,11 +119,19 @@ func (p *Server) ValidateRegistration(previousOptions *protobuf.PushNotification if len(options.Token) == 0 { return nil, ErrMalformedPushNotificationOptionsDeviceToken } - fmt.Println(decryptedPayload) return options, nil } +func (p *Server) HandlePushNotificationOptions(publicKey *ecdsa.PublicKey, payload []byte) error { + + _, err := p.ValidateRegistration(publicKey, payload) + if err != nil { + return err + } + return nil +} + func decrypt(cyphertext []byte, key []byte) ([]byte, error) { if len(cyphertext) < nonceLength { return nil, ErrInvalidCiphertextLength diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index 25de3818b..ee22f3c23 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -1,13 +1,24 @@ package protocol import ( + "crypto/ecdsa" "database/sql" + + "github.com/status-im/status-go/protocol/protobuf" ) -type Persistence struct { +type Persistence interface { + GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) +} + +type SQLitePersistence struct { db *sql.DB } -func NewPersistence(db *sql.DB) *Persistence { - return &Persistence{db: db} +func NewSQLitePersistence(db *sql.DB) Persistence { + return &SQLitePersistence{db: db} +} + +func (p *SQLitePersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) { + return nil, nil } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 023cabdcd..e626505e1 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -1,6 +1,7 @@ package protocol import ( + "crypto/ecdsa" "crypto/rand" "testing" @@ -11,6 +12,14 @@ import ( "github.com/status-im/status-go/protocol/protobuf" ) +type MockPersistence struct { + pno *protobuf.PushNotificationOptions +} + +func (p *MockPersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) { + return p.pno, nil +} + func TestPushNotificationServerValidateRegistration(t *testing.T) { accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" @@ -21,7 +30,8 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { Identity: identity, } - server := Server{config: config} + mockPersistence := &MockPersistence{} + server := New(config, mockPersistence) key, err := crypto.GenerateKey() require.NoError(t, err) @@ -30,35 +40,35 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { require.NoError(t, err) // Empty payload - _, err = server.ValidateRegistration(nil, &key.PublicKey, nil) + _, err = server.ValidateRegistration(&key.PublicKey, nil) require.Equal(t, ErrEmptyPushNotificationOptionsPayload, err) // Empty key - _, err = server.ValidateRegistration(nil, nil, []byte("payload")) + _, err = server.ValidateRegistration(nil, []byte("payload")) require.Equal(t, ErrEmptyPushNotificationOptionsPublicKey, err) // Invalid cyphertext length - _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("too short")) + _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) require.Equal(t, ErrInvalidCiphertextLength, err) // Invalid cyphertext length - _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("too short")) + _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) require.Equal(t, ErrInvalidCiphertextLength, err) // Invalid ciphertext - _, err = server.ValidateRegistration(nil, &key.PublicKey, []byte("not too short but invalid")) + _, err = server.ValidateRegistration(&key.PublicKey, []byte("not too short but invalid")) require.Error(t, ErrInvalidCiphertextLength, err) // Different key ciphertext cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Error(t, err) // Right cyphertext but non unmarshable payload cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrCouldNotUnmarshalPushNotificationOptions, err) // Missing installationID @@ -70,7 +80,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) // Malformed installationID @@ -81,7 +91,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { }) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) // Version set to 0 @@ -93,7 +103,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) // Version lower than previous one @@ -106,12 +116,19 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(&protobuf.PushNotificationOptions{ + + // Setup mock + mockPersistence.pno = &protobuf.PushNotificationOptions{ AccessToken: accessToken, InstallationId: installationID, - Version: 2}, &key.PublicKey, cyphertext) + Version: 2} + + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) + // Cleanup mock + mockPersistence.pno = nil + // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ InstallationId: installationID, @@ -122,7 +139,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Nil(t, err) // Missing access token @@ -134,7 +151,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) // Invalid access token @@ -147,7 +164,7 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) // Missing device token @@ -160,7 +177,6 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) require.NoError(t, err) - _, err = server.ValidateRegistration(nil, &key.PublicKey, cyphertext) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) require.Equal(t, ErrMalformedPushNotificationOptionsDeviceToken, err) - } From 91c7869d9809539c1e76fb87b3ca8820230aa877 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 1 Jul 2020 14:04:09 +0200 Subject: [PATCH 09/46] implement migrations & persistence for pns --- .../1593601728_initial_schema.down.sql | 4 + .../1593601728_initial_schema.up.sql | 11 ++ .../migrations/doc.go | 9 ++ .../push_notification_server.go | 2 +- .../push_notification_server_persistence.go | 42 ++++++- ...sh_notification_server_persistence_test.go | 58 ++++++++++ .../push_notification_server_test.go | 109 ++++++++++-------- protocol/sqlite/migrations.go | 5 + 8 files changed, 189 insertions(+), 51 deletions(-) create mode 100644 protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql create mode 100644 protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql create mode 100644 protocol/push_notification_server/migrations/doc.go create mode 100644 protocol/push_notification_server/push_notification_server_persistence_test.go diff --git a/protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql b/protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql new file mode 100644 index 000000000..dc09dc636 --- /dev/null +++ b/protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql @@ -0,0 +1,4 @@ +DROP TABLE push_notification_server_registrations; +DROP INDEX idx_push_notification_server_registrations_public_key; +DROP INDEX idx_push_notification_server_registrations_public_key_installation_id; + diff --git a/protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql b/protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql new file mode 100644 index 000000000..0c8f38e4c --- /dev/null +++ b/protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS push_notification_server_registrations ( + public_key BLOB NOT NULL, + installation_id VARCHAR NOT NULL, + version INT NOT NULL, + registration BLOB, + UNIQUE(public_key, installation_id) ON CONFLICT REPLACE +); + +CREATE INDEX idx_push_notification_server_registrations_public_key ON push_notification_server_registrations(public_key); +CREATE INDEX idx_push_notification_server_registrations_public_key_installation_id ON push_notification_server_registrations(public_key, installation_id); + diff --git a/protocol/push_notification_server/migrations/doc.go b/protocol/push_notification_server/migrations/doc.go new file mode 100644 index 000000000..0315ccce1 --- /dev/null +++ b/protocol/push_notification_server/migrations/doc.go @@ -0,0 +1,9 @@ +// This file is necessary because "github.com/status-im/migrate/v4" +// can't handle files starting with a prefix. At least that's the case +// for go-bindata. +// If go-bindata is called from the same directory, asset names +// have no prefix and "github.com/status-im/migrate/v4" works as expected. + +package migrations + +//go:generate go-bindata -pkg migrations -o ./migrations.go . diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 3cc6c7ca6..1607fe2db 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -1,4 +1,4 @@ -package protocol +package push_notification_server import ( "errors" diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index ee22f3c23..9f6131d84 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -1,14 +1,22 @@ -package protocol +package push_notification_server import ( "crypto/ecdsa" "database/sql" + "github.com/golang/protobuf/proto" + + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/protocol/protobuf" ) type Persistence interface { + // GetPushNotificationOptions retrieve a push notification options from storage given a public key and installation id GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) + // DeletePushNotificationOptions deletes a push notification options from storage given a public key and installation id + DeletePushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) error + // SavePushNotificationOptions saves a push notification option to the db + SavePushNotificationOptions(publicKey *ecdsa.PublicKey, options *protobuf.PushNotificationOptions) error } type SQLitePersistence struct { @@ -20,5 +28,35 @@ func NewSQLitePersistence(db *sql.DB) Persistence { } func (p *SQLitePersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) { - return nil, nil + var marshaledOptions []byte + err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID).Scan(&marshaledOptions) + + if err == sql.ErrNoRows { + return nil, nil + } else if err != nil { + return nil, err + } + + options := &protobuf.PushNotificationOptions{} + + if err := proto.Unmarshal(marshaledOptions, options); err != nil { + return nil, err + } + return options, nil +} + +func (p *SQLitePersistence) SavePushNotificationOptions(publicKey *ecdsa.PublicKey, options *protobuf.PushNotificationOptions) error { + compressedPublicKey := crypto.CompressPubkey(publicKey) + marshaledOptions, err := proto.Marshal(options) + if err != nil { + return err + } + + _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, compressedPublicKey, options.InstallationId, options.Version, marshaledOptions) + return err +} + +func (p *SQLitePersistence) DeletePushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) error { + _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID) + return err } diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/push_notification_server_persistence_test.go new file mode 100644 index 000000000..1bd0fb10d --- /dev/null +++ b/protocol/push_notification_server/push_notification_server_persistence_test.go @@ -0,0 +1,58 @@ +package push_notification_server + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/golang/protobuf/proto" + "github.com/stretchr/testify/suite" + + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/sqlite" +) + +//tmpFile, err := ioutil.TempFile("", "") + +func TestSQLitePersistenceSuite(t *testing.T) { + suite.Run(t, new(SQLitePersistenceSuite)) +} + +type SQLitePersistenceSuite struct { + suite.Suite + tmpFile *os.File + persistence Persistence +} + +func (s *SQLitePersistenceSuite) SetupTest() { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + s.tmpFile = tmpFile + + database, err := sqlite.Open(s.tmpFile.Name(), "") + s.Require().NoError(err) + s.persistence = NewSQLitePersistence(database) +} + +func (s *SQLitePersistenceSuite) TearDownTest() { + _ = os.Remove(s.tmpFile.Name()) +} + +func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { + key, err := crypto.GenerateKey() + s.Require().NoError(err) + installationID := "54242d02-bb92-11ea-b3de-0242ac130004" + + options := &protobuf.PushNotificationOptions{ + InstallationId: installationID, + Version: 5, + } + + s.Require().NoError(s.persistence.SavePushNotificationOptions(&key.PublicKey, options)) + + retrievedOptions, err := s.persistence.GetPushNotificationOptions(&key.PublicKey, installationID) + s.Require().NoError(err) + + s.Require().True(proto.Equal(options, retrievedOptions)) +} diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index e626505e1..9f4823584 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -1,87 +1,100 @@ -package protocol +package push_notification_server import ( - "crypto/ecdsa" "crypto/rand" + "io/ioutil" + "os" "testing" "github.com/ethereum/go-ethereum/crypto" "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/sqlite" ) -type MockPersistence struct { - pno *protobuf.PushNotificationOptions +func TestServerSuite(t *testing.T) { + suite.Run(t, new(ServerSuite)) } -func (p *MockPersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) { - return p.pno, nil +type ServerSuite struct { + suite.Suite + tmpFile *os.File + persistence Persistence } -func TestPushNotificationServerValidateRegistration(t *testing.T) { +func (s *ServerSuite) SetupTest() { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + s.tmpFile = tmpFile + + database, err := sqlite.Open(s.tmpFile.Name(), "") + s.Require().NoError(err) + s.persistence = NewSQLitePersistence(database) +} + +func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" identity, err := crypto.GenerateKey() - require.NoError(t, err) + s.Require().NoError(err) config := &Config{ Identity: identity, } - mockPersistence := &MockPersistence{} - server := New(config, mockPersistence) + server := New(config, s.persistence) key, err := crypto.GenerateKey() - require.NoError(t, err) + s.Require().NoError(err) sharedKey, err := server.generateSharedKey(&key.PublicKey) - require.NoError(t, err) + s.Require().NoError(err) // Empty payload _, err = server.ValidateRegistration(&key.PublicKey, nil) - require.Equal(t, ErrEmptyPushNotificationOptionsPayload, err) + s.Require().Equal(ErrEmptyPushNotificationOptionsPayload, err) // Empty key _, err = server.ValidateRegistration(nil, []byte("payload")) - require.Equal(t, ErrEmptyPushNotificationOptionsPublicKey, err) + s.Require().Equal(ErrEmptyPushNotificationOptionsPublicKey, err) // Invalid cyphertext length _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) - require.Equal(t, ErrInvalidCiphertextLength, err) + s.Require().Equal(ErrInvalidCiphertextLength, err) // Invalid cyphertext length _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) - require.Equal(t, ErrInvalidCiphertextLength, err) + s.Require().Equal(ErrInvalidCiphertextLength, err) // Invalid ciphertext _, err = server.ValidateRegistration(&key.PublicKey, []byte("not too short but invalid")) - require.Error(t, ErrInvalidCiphertextLength, err) + s.Require().Error(ErrInvalidCiphertextLength, err) // Different key ciphertext cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Error(t, err) + s.Require().Error(err) // Right cyphertext but non unmarshable payload cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrCouldNotUnmarshalPushNotificationOptions, err) + s.Require().Equal(ErrCouldNotUnmarshalPushNotificationOptions, err) // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationOptions{ AccessToken: accessToken, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) + s.Require().Equal(ErrMalformedPushNotificationOptionsInstallationID, err) // Malformed installationID payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -90,21 +103,21 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { Version: 1, }) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrMalformedPushNotificationOptionsInstallationID, err) + s.Require().Equal(ErrMalformedPushNotificationOptionsInstallationID, err) // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ AccessToken: accessToken, InstallationId: installationID, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) + s.Require().Equal(ErrInvalidPushNotificationOptionsVersion, err) // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -112,22 +125,22 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { InstallationId: installationID, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) // Setup mock - mockPersistence.pno = &protobuf.PushNotificationOptions{ + s.Require().NoError(s.persistence.SavePushNotificationOptions(&key.PublicKey, &protobuf.PushNotificationOptions{ AccessToken: accessToken, InstallationId: installationID, - Version: 2} + Version: 2})) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrInvalidPushNotificationOptionsVersion, err) + s.Require().Equal(ErrInvalidPushNotificationOptionsVersion, err) // Cleanup mock - mockPersistence.pno = nil + s.Require().NoError(s.persistence.DeletePushNotificationOptions(&key.PublicKey, installationID)) // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -135,24 +148,24 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { Unregister: true, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Nil(t, err) + s.Require().Nil(err) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ InstallationId: installationID, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) + s.Require().Equal(ErrMalformedPushNotificationOptionsAccessToken, err) // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -160,12 +173,12 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { InstallationId: installationID, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrMalformedPushNotificationOptionsAccessToken, err) + s.Require().Equal(ErrMalformedPushNotificationOptionsAccessToken, err) // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ @@ -173,10 +186,10 @@ func TestPushNotificationServerValidateRegistration(t *testing.T) { InstallationId: installationID, Version: 1, }) - require.NoError(t, err) + s.Require().NoError(err) cyphertext, err = encrypt(payload, sharedKey, rand.Reader) - require.NoError(t, err) + s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - require.Equal(t, ErrMalformedPushNotificationOptionsDeviceToken, err) + s.Require().Equal(ErrMalformedPushNotificationOptionsDeviceToken, err) } diff --git a/protocol/sqlite/migrations.go b/protocol/sqlite/migrations.go index 387f7608d..f082352cd 100644 --- a/protocol/sqlite/migrations.go +++ b/protocol/sqlite/migrations.go @@ -7,6 +7,7 @@ import ( encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations" appmigrations "github.com/status-im/status-go/protocol/migrations" + push_notification_server_migrations "github.com/status-im/status-go/protocol/push_notification_server/migrations" wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations" whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations" ) @@ -35,6 +36,10 @@ var defaultMigrations = []migrationsWithGetter{ Names: appmigrations.AssetNames(), Getter: appmigrations.Asset, }, + { + Names: push_notification_server_migrations.AssetNames(), + Getter: push_notification_server_migrations.Asset, + }, } func prepareMigrations(migrations []migrationsWithGetter) ([]string, getter, error) { From a15919527b47c0591e4d5a614d678a1a420c89a2 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 2 Jul 2020 10:08:19 +0200 Subject: [PATCH 10/46] implement handle push notification registration --- protocol/protobuf/push_notifications.pb.go | 267 +++++++---------- protocol/protobuf/push_notifications.proto | 7 +- .../push_notification.go | 4 +- .../push_notification_test.go | 4 +- protocol/push_notification_server/errors.go | 12 + .../push_notification_server.go | 98 +++--- .../push_notification_server_persistence.go | 32 +- ...sh_notification_server_persistence_test.go | 8 +- .../push_notification_server_test.go | 280 ++++++++++++++++-- 9 files changed, 467 insertions(+), 245 deletions(-) create mode 100644 protocol/push_notification_server/errors.go diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 3d7fda805..3694c30ff 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -20,32 +20,32 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package -type PushNotificationOptions_TokenType int32 +type PushNotificationRegistration_TokenType int32 const ( - PushNotificationOptions_UNKNOWN_TOKEN_TYPE PushNotificationOptions_TokenType = 0 - PushNotificationOptions_APN_TOKEN PushNotificationOptions_TokenType = 1 - PushNotificationOptions_FIREBASE_TOKEN PushNotificationOptions_TokenType = 2 + PushNotificationRegistration_UNKNOWN_TOKEN_TYPE PushNotificationRegistration_TokenType = 0 + PushNotificationRegistration_APN_TOKEN PushNotificationRegistration_TokenType = 1 + PushNotificationRegistration_FIREBASE_TOKEN PushNotificationRegistration_TokenType = 2 ) -var PushNotificationOptions_TokenType_name = map[int32]string{ +var PushNotificationRegistration_TokenType_name = map[int32]string{ 0: "UNKNOWN_TOKEN_TYPE", 1: "APN_TOKEN", 2: "FIREBASE_TOKEN", } -var PushNotificationOptions_TokenType_value = map[string]int32{ +var PushNotificationRegistration_TokenType_value = map[string]int32{ "UNKNOWN_TOKEN_TYPE": 0, "APN_TOKEN": 1, "FIREBASE_TOKEN": 2, } -func (x PushNotificationOptions_TokenType) String() string { - return proto.EnumName(PushNotificationOptions_TokenType_name, int32(x)) +func (x PushNotificationRegistration_TokenType) String() string { + return proto.EnumName(PushNotificationRegistration_TokenType_name, int32(x)) } -func (PushNotificationOptions_TokenType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{1, 0} +func (PushNotificationRegistration_TokenType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{0, 0} } type PushNotificationRegistrationResponse_ErrorType int32 @@ -79,153 +79,106 @@ func (x PushNotificationRegistrationResponse_ErrorType) String() string { } func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{2, 0} + return fileDescriptor_200acd86044eaa5d, []int{1, 0} } -type PushNotificationTokenPair struct { - Token []byte `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type PushNotificationRegistration struct { + TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` + Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *PushNotificationTokenPair) Reset() { *m = PushNotificationTokenPair{} } -func (m *PushNotificationTokenPair) String() string { return proto.CompactTextString(m) } -func (*PushNotificationTokenPair) ProtoMessage() {} -func (*PushNotificationTokenPair) Descriptor() ([]byte, []int) { +func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} } +func (m *PushNotificationRegistration) String() string { return proto.CompactTextString(m) } +func (*PushNotificationRegistration) ProtoMessage() {} +func (*PushNotificationRegistration) Descriptor() ([]byte, []int) { return fileDescriptor_200acd86044eaa5d, []int{0} } -func (m *PushNotificationTokenPair) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationTokenPair.Unmarshal(m, b) +func (m *PushNotificationRegistration) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationRegistration.Unmarshal(m, b) } -func (m *PushNotificationTokenPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationTokenPair.Marshal(b, m, deterministic) +func (m *PushNotificationRegistration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationRegistration.Marshal(b, m, deterministic) } -func (m *PushNotificationTokenPair) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationTokenPair.Merge(m, src) +func (m *PushNotificationRegistration) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationRegistration.Merge(m, src) } -func (m *PushNotificationTokenPair) XXX_Size() int { - return xxx_messageInfo_PushNotificationTokenPair.Size(m) +func (m *PushNotificationRegistration) XXX_Size() int { + return xxx_messageInfo_PushNotificationRegistration.Size(m) } -func (m *PushNotificationTokenPair) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationTokenPair.DiscardUnknown(m) +func (m *PushNotificationRegistration) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationRegistration.DiscardUnknown(m) } -var xxx_messageInfo_PushNotificationTokenPair proto.InternalMessageInfo +var xxx_messageInfo_PushNotificationRegistration proto.InternalMessageInfo -func (m *PushNotificationTokenPair) GetToken() []byte { - if m != nil { - return m.Token - } - return nil -} - -func (m *PushNotificationTokenPair) GetPublicKey() []byte { - if m != nil { - return m.PublicKey - } - return nil -} - -type PushNotificationOptions struct { - TokenType PushNotificationOptions_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationOptions_TokenType" json:"token_type,omitempty"` - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` - AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` - Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` - AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` - BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` - Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushNotificationOptions) Reset() { *m = PushNotificationOptions{} } -func (m *PushNotificationOptions) String() string { return proto.CompactTextString(m) } -func (*PushNotificationOptions) ProtoMessage() {} -func (*PushNotificationOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{1} -} - -func (m *PushNotificationOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationOptions.Unmarshal(m, b) -} -func (m *PushNotificationOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationOptions.Marshal(b, m, deterministic) -} -func (m *PushNotificationOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationOptions.Merge(m, src) -} -func (m *PushNotificationOptions) XXX_Size() int { - return xxx_messageInfo_PushNotificationOptions.Size(m) -} -func (m *PushNotificationOptions) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_PushNotificationOptions proto.InternalMessageInfo - -func (m *PushNotificationOptions) GetTokenType() PushNotificationOptions_TokenType { +func (m *PushNotificationRegistration) GetTokenType() PushNotificationRegistration_TokenType { if m != nil { return m.TokenType } - return PushNotificationOptions_UNKNOWN_TOKEN_TYPE + return PushNotificationRegistration_UNKNOWN_TOKEN_TYPE } -func (m *PushNotificationOptions) GetToken() string { +func (m *PushNotificationRegistration) GetToken() string { if m != nil { return m.Token } return "" } -func (m *PushNotificationOptions) GetInstallationId() string { +func (m *PushNotificationRegistration) GetInstallationId() string { if m != nil { return m.InstallationId } return "" } -func (m *PushNotificationOptions) GetAccessToken() string { +func (m *PushNotificationRegistration) GetAccessToken() string { if m != nil { return m.AccessToken } return "" } -func (m *PushNotificationOptions) GetEnabled() bool { +func (m *PushNotificationRegistration) GetEnabled() bool { if m != nil { return m.Enabled } return false } -func (m *PushNotificationOptions) GetVersion() uint64 { +func (m *PushNotificationRegistration) GetVersion() uint64 { if m != nil { return m.Version } return 0 } -func (m *PushNotificationOptions) GetAllowedUserList() [][]byte { +func (m *PushNotificationRegistration) GetAllowedUserList() [][]byte { if m != nil { return m.AllowedUserList } return nil } -func (m *PushNotificationOptions) GetBlockedChatList() [][]byte { +func (m *PushNotificationRegistration) GetBlockedChatList() [][]byte { if m != nil { return m.BlockedChatList } return nil } -func (m *PushNotificationOptions) GetUnregister() bool { +func (m *PushNotificationRegistration) GetUnregister() bool { if m != nil { return m.Unregister } @@ -245,7 +198,7 @@ func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotifi func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationRegistrationResponse) ProtoMessage() {} func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{2} + return fileDescriptor_200acd86044eaa5d, []int{1} } func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error { @@ -300,7 +253,7 @@ func (m *PushNotificationAdvertisementInfo) Reset() { *m = PushNotificat func (m *PushNotificationAdvertisementInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationAdvertisementInfo) ProtoMessage() {} func (*PushNotificationAdvertisementInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{3} + return fileDescriptor_200acd86044eaa5d, []int{2} } func (m *PushNotificationAdvertisementInfo) XXX_Unmarshal(b []byte) error { @@ -353,7 +306,7 @@ func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertiseme func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) } func (*ContactCodeAdvertisement) ProtoMessage() {} func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{4} + return fileDescriptor_200acd86044eaa5d, []int{3} } func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error { @@ -392,7 +345,7 @@ func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} } func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) } func (*PushNotificationQuery) ProtoMessage() {} func (*PushNotificationQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{5} + return fileDescriptor_200acd86044eaa5d, []int{4} } func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error { @@ -434,7 +387,7 @@ func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQuery func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryInfo) ProtoMessage() {} func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{6} + return fileDescriptor_200acd86044eaa5d, []int{5} } func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error { @@ -494,7 +447,7 @@ func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQ func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryResponse) ProtoMessage() {} func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{7} + return fileDescriptor_200acd86044eaa5d, []int{6} } func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error { @@ -534,7 +487,7 @@ func (m *PushNotification) Reset() { *m = PushNotification{} } func (m *PushNotification) String() string { return proto.CompactTextString(m) } func (*PushNotification) ProtoMessage() {} func (*PushNotification) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{8} + return fileDescriptor_200acd86044eaa5d, []int{7} } func (m *PushNotification) XXX_Unmarshal(b []byte) error { @@ -583,7 +536,7 @@ func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) } func (*PushNotificationRequest) ProtoMessage() {} func (*PushNotificationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{9} + return fileDescriptor_200acd86044eaa5d, []int{8} } func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error { @@ -643,7 +596,7 @@ func (m *PushNotificationAcknowledgement) Reset() { *m = PushNotificatio func (m *PushNotificationAcknowledgement) String() string { return proto.CompactTextString(m) } func (*PushNotificationAcknowledgement) ProtoMessage() {} func (*PushNotificationAcknowledgement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{10} + return fileDescriptor_200acd86044eaa5d, []int{9} } func (m *PushNotificationAcknowledgement) XXX_Unmarshal(b []byte) error { @@ -672,10 +625,9 @@ func (m *PushNotificationAcknowledgement) GetId() string { } func init() { - proto.RegisterEnum("protobuf.PushNotificationOptions_TokenType", PushNotificationOptions_TokenType_name, PushNotificationOptions_TokenType_value) + proto.RegisterEnum("protobuf.PushNotificationRegistration_TokenType", PushNotificationRegistration_TokenType_name, PushNotificationRegistration_TokenType_value) proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value) - proto.RegisterType((*PushNotificationTokenPair)(nil), "protobuf.PushNotificationTokenPair") - proto.RegisterType((*PushNotificationOptions)(nil), "protobuf.PushNotificationOptions") + proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration") proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement") @@ -690,54 +642,53 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 781 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdd, 0x6e, 0xe2, 0x46, - 0x14, 0xae, 0x0d, 0x49, 0xf0, 0x09, 0x25, 0x64, 0x94, 0x1f, 0x37, 0x52, 0x1a, 0xe2, 0x56, 0x2a, - 0x6a, 0x25, 0xa4, 0xa6, 0x52, 0x9b, 0x5b, 0x4a, 0x9c, 0xd6, 0x4d, 0x30, 0x74, 0x80, 0xfe, 0x5c, - 0x59, 0xc6, 0x1e, 0xc2, 0x08, 0xc7, 0xf6, 0x7a, 0xc6, 0x89, 0xb8, 0x58, 0x69, 0x9f, 0x60, 0xdf, - 0x64, 0x6f, 0x72, 0xbb, 0x2f, 0xb7, 0xf2, 0xd8, 0x06, 0x62, 0x48, 0x94, 0x2b, 0x7c, 0xce, 0x9c, - 0xdf, 0xef, 0x9c, 0xef, 0x00, 0x6a, 0x18, 0xb3, 0xa9, 0xe5, 0x07, 0x9c, 0x4e, 0xa8, 0x63, 0x73, - 0x1a, 0xf8, 0xac, 0x15, 0x46, 0x01, 0x0f, 0x50, 0x45, 0xfc, 0x8c, 0xe3, 0x89, 0xd6, 0x87, 0x6f, - 0xfa, 0x31, 0x9b, 0x9a, 0x2b, 0x46, 0xc3, 0x60, 0x46, 0xfc, 0xbe, 0x4d, 0x23, 0x74, 0x00, 0x5b, - 0x3c, 0x11, 0x54, 0xa9, 0x21, 0x35, 0xab, 0x38, 0x15, 0xd0, 0x29, 0x40, 0x18, 0x8f, 0x3d, 0xea, - 0x58, 0x33, 0x32, 0x57, 0x65, 0xf1, 0xa4, 0xa4, 0x9a, 0x1b, 0x32, 0xd7, 0x3e, 0x97, 0xe0, 0xb8, - 0x18, 0xb2, 0x17, 0x8a, 0xec, 0xe8, 0x2f, 0x00, 0x11, 0xc3, 0xe2, 0xf3, 0x90, 0x88, 0xa8, 0xb5, - 0x8b, 0x9f, 0x5a, 0x79, 0x31, 0xad, 0x17, 0xdc, 0x5a, 0xa2, 0xa2, 0xe1, 0x3c, 0x24, 0x58, 0xe1, - 0xf9, 0xe7, 0xb2, 0xb8, 0xa4, 0x02, 0x25, 0x2f, 0xee, 0x07, 0xd8, 0xa3, 0x3e, 0xe3, 0xb6, 0xe7, - 0x89, 0x08, 0x16, 0x75, 0xd5, 0x92, 0x78, 0xaf, 0xad, 0xaa, 0x0d, 0x17, 0x9d, 0x43, 0xd5, 0x76, - 0x1c, 0xc2, 0x98, 0x95, 0x46, 0x29, 0x0b, 0xab, 0xdd, 0x54, 0x27, 0x12, 0x22, 0x15, 0x76, 0x88, - 0x6f, 0x8f, 0x3d, 0xe2, 0xaa, 0x5b, 0x0d, 0xa9, 0x59, 0xc1, 0xb9, 0x98, 0xbc, 0x3c, 0x90, 0x88, - 0xd1, 0xc0, 0x57, 0xb7, 0x1b, 0x52, 0xb3, 0x8c, 0x73, 0x11, 0xfd, 0x08, 0xfb, 0xb6, 0xe7, 0x05, - 0x8f, 0xc4, 0xb5, 0x62, 0x46, 0x22, 0xcb, 0xa3, 0x8c, 0xab, 0x3b, 0x8d, 0x52, 0xb3, 0x8a, 0xf7, - 0xb2, 0x87, 0x11, 0x23, 0xd1, 0x2d, 0x65, 0x3c, 0xb1, 0x1d, 0x7b, 0x81, 0x33, 0x23, 0xae, 0xe5, - 0x4c, 0x6d, 0x9e, 0xda, 0x56, 0x52, 0xdb, 0xec, 0xa1, 0x33, 0xb5, 0xb9, 0xb0, 0xfd, 0x16, 0x20, - 0xf6, 0x23, 0x72, 0x47, 0x19, 0x27, 0x91, 0xaa, 0x88, 0x72, 0x56, 0x34, 0xda, 0x35, 0x28, 0x0b, - 0x94, 0xd0, 0x11, 0xa0, 0x91, 0x79, 0x63, 0xf6, 0xfe, 0x35, 0xad, 0x61, 0xef, 0x46, 0x37, 0xad, - 0xe1, 0xff, 0x7d, 0xbd, 0xfe, 0x15, 0xfa, 0x1a, 0x94, 0x76, 0x3f, 0xd3, 0xd5, 0x25, 0x84, 0xa0, - 0x76, 0x6d, 0x60, 0xfd, 0xf7, 0xf6, 0x40, 0xcf, 0x74, 0xb2, 0xf6, 0x24, 0xc3, 0xf7, 0xc5, 0x31, - 0x60, 0x91, 0x24, 0xca, 0xbe, 0x59, 0x18, 0xf8, 0x8c, 0x24, 0x10, 0xb0, 0x58, 0x80, 0x25, 0xe6, - 0x58, 0xc1, 0xb9, 0x88, 0x4c, 0xd8, 0x22, 0x51, 0x14, 0x44, 0x62, 0x30, 0xb5, 0x8b, 0xcb, 0x97, - 0xe7, 0xbb, 0x29, 0x70, 0x4b, 0x4f, 0x7c, 0xc5, 0xb0, 0xd3, 0x30, 0xc9, 0xbe, 0x45, 0xe4, 0x5d, - 0x4c, 0x18, 0xcf, 0xa7, 0x59, 0xc5, 0x4a, 0xa6, 0x31, 0x5c, 0xed, 0x83, 0x04, 0xca, 0xc2, 0x67, - 0xb5, 0x75, 0x1d, 0xe3, 0x1e, 0xce, 0x5b, 0x3f, 0x84, 0xfd, 0x6e, 0xfb, 0xf6, 0xba, 0x87, 0xbb, - 0xfa, 0x95, 0xd5, 0xd5, 0x07, 0x83, 0xf6, 0x1f, 0x7a, 0x5d, 0x42, 0x07, 0x50, 0xff, 0x47, 0xc7, - 0x03, 0xa3, 0x67, 0x5a, 0x5d, 0x63, 0xd0, 0x6d, 0x0f, 0x3b, 0x7f, 0xd6, 0x65, 0x74, 0x02, 0x47, - 0x23, 0x73, 0x30, 0xea, 0xf7, 0x7b, 0x78, 0xa8, 0x5f, 0xad, 0x62, 0x58, 0x4a, 0x40, 0x33, 0xcc, - 0xa1, 0x8e, 0xcd, 0xf6, 0x6d, 0x9a, 0xa1, 0x5e, 0xd6, 0x3e, 0x4a, 0x70, 0x5e, 0xec, 0xad, 0xed, - 0x3e, 0x90, 0x88, 0x53, 0x46, 0xee, 0x89, 0xcf, 0x0d, 0x7f, 0x12, 0x14, 0x78, 0x23, 0x15, 0x78, - 0xb3, 0xb6, 0x90, 0xf2, 0xfa, 0x42, 0xbe, 0x75, 0xb9, 0xb5, 0xf7, 0xa0, 0x76, 0x02, 0x9f, 0xdb, - 0x0e, 0xef, 0x04, 0x2e, 0x79, 0x56, 0x0a, 0xb2, 0xe1, 0x68, 0xed, 0x2e, 0x58, 0xd4, 0x9f, 0x04, - 0xaa, 0xd4, 0x28, 0x35, 0x77, 0x5f, 0xe3, 0xe3, 0x5a, 0x4f, 0xf8, 0x20, 0x2c, 0x98, 0x24, 0x5a, - 0xed, 0x12, 0x0e, 0x8b, 0xae, 0x7f, 0xc7, 0x24, 0x9a, 0xa3, 0x33, 0xd8, 0x5d, 0x42, 0xc0, 0x44, - 0xc2, 0x2a, 0x86, 0x05, 0x06, 0x4c, 0x7b, 0x92, 0xd6, 0xef, 0x91, 0x70, 0x15, 0x08, 0x16, 0x21, - 0x92, 0xde, 0x04, 0x91, 0xbc, 0x91, 0xff, 0xcf, 0xa7, 0x51, 0x2a, 0x4e, 0x63, 0x23, 0x8f, 0xcb, - 0xc2, 0xaa, 0xc8, 0x63, 0xed, 0x3f, 0x38, 0xdd, 0x58, 0xf3, 0x82, 0x2b, 0xbf, 0x41, 0x79, 0x05, - 0xe0, 0xef, 0x5e, 0x06, 0x78, 0xd1, 0x2a, 0x16, 0x0e, 0x9a, 0x09, 0xf5, 0xa2, 0xc9, 0x5b, 0x40, - 0x38, 0x86, 0x1d, 0x71, 0x50, 0x16, 0xcd, 0x6f, 0x27, 0xa2, 0xe1, 0x6a, 0x9f, 0xa4, 0xf5, 0xdb, - 0x8c, 0x53, 0x26, 0xa1, 0x5f, 0xa1, 0x92, 0x91, 0x8a, 0x65, 0x85, 0x9e, 0xbc, 0xc2, 0xdc, 0x85, - 0x6d, 0x72, 0x08, 0xee, 0x09, 0x63, 0xf6, 0x1d, 0xc9, 0xfe, 0x0b, 0x72, 0x31, 0x81, 0x38, 0xfb, - 0x5c, 0x6e, 0xaa, 0x92, 0x69, 0xf2, 0x0b, 0x3c, 0xb3, 0x92, 0x40, 0x34, 0x22, 0xee, 0xf2, 0x02, - 0xcf, 0x70, 0xa6, 0xd2, 0x7e, 0x86, 0xb3, 0xb5, 0x1d, 0x74, 0x66, 0x7e, 0xf0, 0xe8, 0x11, 0xf7, - 0x2e, 0x5d, 0xe7, 0x1a, 0xc8, 0xd4, 0xcd, 0x40, 0x90, 0xa9, 0x3b, 0xde, 0x16, 0x35, 0xff, 0xf2, - 0x25, 0x00, 0x00, 0xff, 0xff, 0x01, 0x0b, 0xdd, 0xd4, 0xfd, 0x06, 0x00, 0x00, + // 763 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xdd, 0x6e, 0xda, 0x48, + 0x14, 0x5e, 0x1b, 0x92, 0xe0, 0x13, 0x96, 0x90, 0x51, 0x7e, 0xbc, 0xd1, 0x66, 0x43, 0xbc, 0x2b, + 0x2d, 0x6a, 0x25, 0xd4, 0xa6, 0x52, 0x9b, 0x5b, 0x4a, 0x9c, 0xd6, 0x4a, 0x30, 0x74, 0x80, 0xfe, + 0x5c, 0x59, 0xc6, 0x1e, 0x82, 0x85, 0xe3, 0xa1, 0x9e, 0x71, 0x22, 0x2e, 0x2a, 0xf5, 0x09, 0xfa, + 0x26, 0xbd, 0xc9, 0x3b, 0xf4, 0xbd, 0x2a, 0x8f, 0x6d, 0x42, 0x80, 0x44, 0x5c, 0xd9, 0xe7, 0x9b, + 0xef, 0xcc, 0xcc, 0xf9, 0xce, 0xf9, 0x06, 0xd4, 0x71, 0xc4, 0x86, 0x56, 0x40, 0xb9, 0x37, 0xf0, + 0x1c, 0x9b, 0x7b, 0x34, 0x60, 0xb5, 0x71, 0x48, 0x39, 0x45, 0x05, 0xf1, 0xe9, 0x47, 0x03, 0xed, + 0x57, 0x0e, 0xfe, 0x6e, 0x47, 0x6c, 0x68, 0xce, 0xb0, 0x30, 0xb9, 0xf2, 0x18, 0x0f, 0xc5, 0x3f, + 0x6a, 0x01, 0x70, 0x3a, 0x22, 0x81, 0xc5, 0x27, 0x63, 0xa2, 0x4a, 0x15, 0xa9, 0x5a, 0x3a, 0x79, + 0x51, 0xcb, 0xf2, 0x6b, 0x4f, 0xe5, 0xd6, 0xba, 0x71, 0x62, 0x77, 0x32, 0x26, 0x58, 0xe1, 0xd9, + 0x2f, 0xda, 0x81, 0x35, 0x11, 0xa8, 0x72, 0x45, 0xaa, 0x2a, 0x38, 0x09, 0xd0, 0xff, 0xb0, 0xe5, + 0x05, 0x8c, 0xdb, 0xbe, 0x2f, 0x52, 0x2d, 0xcf, 0x55, 0x73, 0x62, 0xbd, 0x34, 0x0b, 0x1b, 0x2e, + 0x3a, 0x86, 0xa2, 0xed, 0x38, 0x84, 0x31, 0x2b, 0xd9, 0x25, 0x2f, 0x58, 0x9b, 0x09, 0x26, 0x0e, + 0x44, 0x2a, 0x6c, 0x90, 0xc0, 0xee, 0xfb, 0xc4, 0x55, 0xd7, 0x2a, 0x52, 0xb5, 0x80, 0xb3, 0x30, + 0x5e, 0xb9, 0x21, 0x21, 0xf3, 0x68, 0xa0, 0xae, 0x57, 0xa4, 0x6a, 0x1e, 0x67, 0x21, 0x7a, 0x06, + 0xdb, 0xb6, 0xef, 0xd3, 0x5b, 0xe2, 0x5a, 0x11, 0x23, 0xa1, 0xe5, 0x7b, 0x8c, 0xab, 0x1b, 0x95, + 0x5c, 0xb5, 0x88, 0xb7, 0xd2, 0x85, 0x1e, 0x23, 0xe1, 0xa5, 0xc7, 0x78, 0xcc, 0xed, 0xfb, 0xd4, + 0x19, 0x11, 0xd7, 0x72, 0x86, 0x36, 0x4f, 0xb8, 0x85, 0x84, 0x9b, 0x2e, 0x34, 0x86, 0x36, 0x17, + 0xdc, 0x7f, 0x00, 0xa2, 0x20, 0x14, 0xa2, 0x90, 0x50, 0x55, 0xc4, 0x75, 0x66, 0x10, 0xed, 0x1c, + 0x94, 0xa9, 0x4a, 0x68, 0x0f, 0x50, 0xcf, 0xbc, 0x30, 0x5b, 0x9f, 0x4c, 0xab, 0xdb, 0xba, 0xd0, + 0x4d, 0xab, 0xfb, 0xa5, 0xad, 0x97, 0xff, 0x40, 0x7f, 0x82, 0x52, 0x6f, 0xa7, 0x58, 0x59, 0x42, + 0x08, 0x4a, 0xe7, 0x06, 0xd6, 0xdf, 0xd6, 0x3b, 0x7a, 0x8a, 0xc9, 0xda, 0x9d, 0x0c, 0xff, 0x3d, + 0xd5, 0x0b, 0x4c, 0xd8, 0x98, 0x06, 0x8c, 0xc4, 0x12, 0xb0, 0x48, 0x88, 0x25, 0x9a, 0x59, 0xc0, + 0x59, 0x88, 0x4c, 0x58, 0x23, 0x61, 0x48, 0x43, 0xd1, 0x98, 0xd2, 0xc9, 0xe9, 0x6a, 0x4d, 0xce, + 0x36, 0xae, 0xe9, 0x71, 0xae, 0x68, 0x76, 0xb2, 0x0d, 0x3a, 0x04, 0x08, 0xc9, 0xd7, 0x88, 0x30, + 0x9e, 0x75, 0xb3, 0x88, 0x95, 0x14, 0x31, 0x5c, 0xed, 0xbb, 0x04, 0xca, 0x34, 0x67, 0xb6, 0x74, + 0x1d, 0xe3, 0x16, 0xce, 0x4a, 0xdf, 0x85, 0xed, 0x66, 0xfd, 0xf2, 0xbc, 0x85, 0x9b, 0xfa, 0x99, + 0xd5, 0xd4, 0x3b, 0x9d, 0xfa, 0x3b, 0xbd, 0x2c, 0xa1, 0x1d, 0x28, 0x7f, 0xd4, 0x71, 0xc7, 0x68, + 0x99, 0x56, 0xd3, 0xe8, 0x34, 0xeb, 0xdd, 0xc6, 0xfb, 0xb2, 0x8c, 0x0e, 0x60, 0xaf, 0x67, 0x76, + 0x7a, 0xed, 0x76, 0x0b, 0x77, 0xf5, 0xb3, 0x59, 0x0d, 0x73, 0xb1, 0x68, 0x86, 0xd9, 0xd5, 0xb1, + 0x59, 0xbf, 0x4c, 0x4e, 0x28, 0xe7, 0xb5, 0x1f, 0x12, 0x1c, 0xcf, 0xd7, 0x56, 0x77, 0x6f, 0x48, + 0xc8, 0x3d, 0x46, 0xae, 0x49, 0xc0, 0x8d, 0x60, 0x40, 0xe3, 0x3a, 0xc6, 0x51, 0xdf, 0xf7, 0x1c, + 0x6b, 0x44, 0x26, 0x42, 0xb4, 0x22, 0x56, 0x12, 0xe4, 0x82, 0x4c, 0x16, 0x06, 0x52, 0x5e, 0x1c, + 0xc8, 0x55, 0x87, 0x5b, 0xfb, 0x06, 0x6a, 0x83, 0x06, 0xdc, 0x76, 0x78, 0x83, 0xba, 0xe4, 0xc1, + 0x55, 0x90, 0x0d, 0x7b, 0x0b, 0x7e, 0xb6, 0xbc, 0x60, 0x40, 0x55, 0xa9, 0x92, 0xab, 0x6e, 0x9e, + 0x3c, 0x7f, 0xbc, 0x5f, 0x0b, 0x35, 0xe1, 0x9d, 0xf1, 0x1c, 0x25, 0x46, 0xb5, 0x53, 0xd8, 0x9d, + 0x4f, 0xfd, 0x10, 0x91, 0x70, 0x82, 0x8e, 0x60, 0xf3, 0x5e, 0x02, 0x26, 0x0e, 0x2c, 0x62, 0x98, + 0x6a, 0xc0, 0xb4, 0x3b, 0x09, 0xfe, 0x5a, 0x9a, 0x2a, 0x14, 0x9c, 0x97, 0x48, 0x5a, 0x49, 0x22, + 0x79, 0xa9, 0xff, 0x1f, 0x76, 0x23, 0x37, 0xdf, 0x8d, 0xa5, 0x3e, 0xce, 0x0b, 0xd6, 0xbc, 0x8f, + 0xb5, 0xcf, 0x70, 0xb8, 0xf4, 0xce, 0x53, 0xaf, 0xbc, 0x81, 0xfc, 0x8c, 0xc0, 0xff, 0x3e, 0x2e, + 0xf0, 0xb4, 0x54, 0x2c, 0x12, 0x34, 0x13, 0xca, 0xf3, 0x94, 0x55, 0x44, 0xd8, 0x87, 0x0d, 0xf1, + 0xa0, 0x4c, 0x8b, 0x5f, 0x8f, 0x43, 0xc3, 0xd5, 0x7e, 0x4a, 0xb0, 0xbf, 0x68, 0x42, 0xe1, 0x24, + 0xf4, 0x1a, 0x0a, 0xa9, 0xa9, 0x58, 0x7a, 0xd1, 0x83, 0x27, 0x9c, 0x3b, 0xe5, 0xc6, 0x0f, 0xc1, + 0x35, 0x61, 0xcc, 0xbe, 0x22, 0xe2, 0xb0, 0x22, 0xce, 0xc2, 0x58, 0xe2, 0xf4, 0xf7, 0x7e, 0x52, + 0x95, 0x14, 0xc9, 0x5e, 0xe0, 0x91, 0x15, 0x6f, 0xe4, 0x85, 0xc4, 0xbd, 0x7f, 0x81, 0x47, 0x38, + 0x85, 0xb4, 0x97, 0x70, 0xb4, 0x30, 0x83, 0xce, 0x28, 0xa0, 0xb7, 0x3e, 0x71, 0xaf, 0x92, 0x71, + 0x2e, 0x81, 0xec, 0xb9, 0xa9, 0x08, 0xb2, 0xe7, 0xf6, 0xd7, 0xc5, 0x9d, 0x5f, 0xfd, 0x0e, 0x00, + 0x00, 0xff, 0xff, 0x6a, 0xc9, 0xcf, 0x6e, 0xb5, 0x06, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index d552c11b3..21f11353c 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -2,12 +2,7 @@ syntax = "proto3"; package protobuf; -message PushNotificationTokenPair { - bytes token = 1; - bytes public_key = 2; -} - -message PushNotificationOptions { +message PushNotificationRegistration { enum TokenType { UNKNOWN_TOKEN_TYPE = 0; APN_TOKEN = 1; diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index eb7354ddd..ceae98e03 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -126,14 +126,14 @@ func (p *Client) allowedUserList(token []byte) ([][]byte, error) { return encryptedTokens, nil } -func (p *Client) buildPushNotificationOptionsMessage() (*protobuf.PushNotificationOptions, error) { +func (p *Client) buildPushNotificationRegistrationMessage() (*protobuf.PushNotificationRegistration, error) { token := uuid.New().String() allowedUserList, err := p.allowedUserList([]byte(token)) if err != nil { return nil, err } - options := &protobuf.PushNotificationOptions{ + options := &protobuf.PushNotificationRegistration{ AccessToken: token, Version: p.lastPushNotificationVersion + 1, InstallationId: p.config.InstallationID, diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index 6ae4a1b29..d0b058a16 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -67,7 +67,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { // Set reader client.reader = bytes.NewReader([]byte(expectedUUID)) - options := &protobuf.PushNotificationOptions{ + options := &protobuf.PushNotificationRegistration{ Version: 1, AccessToken: expectedUUID, Token: myDeviceToken, @@ -77,7 +77,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { AllowedUserList: [][]byte{encryptedToken}, } - actualMessage, err := client.buildPushNotificationOptionsMessage() + actualMessage, err := client.buildPushNotificationRegistrationMessage() require.NoError(t, err) require.Equal(t, options, actualMessage) diff --git a/protocol/push_notification_server/errors.go b/protocol/push_notification_server/errors.go new file mode 100644 index 000000000..69b24a837 --- /dev/null +++ b/protocol/push_notification_server/errors.go @@ -0,0 +1,12 @@ +package push_notification_server + +import "errors" + +var ErrInvalidPushNotificationRegistrationVersion = errors.New("invalid version") +var ErrEmptyPushNotificationRegistrationPayload = errors.New("empty payload") +var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid installationID") +var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key") +var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences") +var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") +var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token") +var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token") diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 1607fe2db..ed809d6ec 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -7,6 +7,7 @@ import ( "crypto/aes" "crypto/cipher" "crypto/ecdsa" + "golang.org/x/crypto/sha3" "github.com/golang/protobuf/proto" "github.com/google/uuid" @@ -18,15 +19,6 @@ import ( const encryptedPayloadKeyLength = 16 const nonceLength = 12 -var ErrInvalidPushNotificationOptionsVersion = errors.New("invalid version") -var ErrEmptyPushNotificationOptionsPayload = errors.New("empty payload") -var ErrMalformedPushNotificationOptionsInstallationID = errors.New("invalid installationID") -var ErrEmptyPushNotificationOptionsPublicKey = errors.New("no public key") -var ErrCouldNotUnmarshalPushNotificationOptions = errors.New("could not unmarshal preferences") -var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") -var ErrMalformedPushNotificationOptionsAccessToken = errors.New("invalid access token") -var ErrMalformedPushNotificationOptionsDeviceToken = errors.New("invalid device token") - type Config struct { // Identity is our identity key Identity *ecdsa.PrivateKey @@ -70,13 +62,13 @@ func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) // ValidateRegistration validates a new message against the last one received for a given installationID and and public key // and return the decrypted message -func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationOptions, error) { +func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) { if payload == nil { - return nil, ErrEmptyPushNotificationOptionsPayload + return nil, ErrEmptyPushNotificationRegistrationPayload } if publicKey == nil { - return nil, ErrEmptyPushNotificationOptionsPublicKey + return nil, ErrEmptyPushNotificationRegistrationPublicKey } decryptedPayload, err := p.decryptRegistration(publicKey, payload) @@ -84,52 +76,82 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, err } - options := &protobuf.PushNotificationOptions{} + registration := &protobuf.PushNotificationRegistration{} - if err := proto.Unmarshal(decryptedPayload, options); err != nil { - return nil, ErrCouldNotUnmarshalPushNotificationOptions + if err := proto.Unmarshal(decryptedPayload, registration); err != nil { + return nil, ErrCouldNotUnmarshalPushNotificationRegistration } - if options.Version < 1 { - return nil, ErrInvalidPushNotificationOptionsVersion + if registration.Version < 1 { + return nil, ErrInvalidPushNotificationRegistrationVersion } - if err := p.validateUUID(options.InstallationId); err != nil { - return nil, ErrMalformedPushNotificationOptionsInstallationID + if err := p.validateUUID(registration.InstallationId); err != nil { + return nil, ErrMalformedPushNotificationRegistrationInstallationID } - previousOptions, err := p.persistence.GetPushNotificationOptions(publicKey, options.InstallationId) + previousRegistration, err := p.persistence.GetPushNotificationRegistration(publicKey, registration.InstallationId) if err != nil { return nil, err } - if previousOptions != nil && options.Version <= previousOptions.Version { - return nil, ErrInvalidPushNotificationOptionsVersion + if previousRegistration != nil && registration.Version <= previousRegistration.Version { + return nil, ErrInvalidPushNotificationRegistrationVersion } // Unregistering message - if options.Unregister { - return options, nil + if registration.Unregister { + return registration, nil } - if err := p.validateUUID(options.AccessToken); err != nil { - return nil, ErrMalformedPushNotificationOptionsAccessToken + if err := p.validateUUID(registration.AccessToken); err != nil { + return nil, ErrMalformedPushNotificationRegistrationAccessToken } - if len(options.Token) == 0 { - return nil, ErrMalformedPushNotificationOptionsDeviceToken + if len(registration.Token) == 0 { + return nil, ErrMalformedPushNotificationRegistrationDeviceToken } - return options, nil + return registration, nil } -func (p *Server) HandlePushNotificationOptions(publicKey *ecdsa.PublicKey, payload []byte) error { - - _, err := p.ValidateRegistration(publicKey, payload) - if err != nil { - return err +func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { + response := &protobuf.PushNotificationRegistrationResponse{ + RequestId: shake256(payload), } - return nil + + registration, err := p.ValidateRegistration(publicKey, payload) + if registration != nil { + } + + if err != nil { + if err == ErrInvalidPushNotificationRegistrationVersion { + response.Error = protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH + } else { + response.Error = protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE + } + return response + } + + if registration.Unregister { + // We save an empty registration, only keeping version and installation-id + emptyRegistration := &protobuf.PushNotificationRegistration{ + Version: registration.Version, + InstallationId: registration.InstallationId, + } + if err := p.persistence.SavePushNotificationRegistration(publicKey, emptyRegistration); err != nil { + response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR + return response + } + + } else if err := p.persistence.SavePushNotificationRegistration(publicKey, registration); err != nil { + response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR + return response + } + + response.Success = true + + return response } func decrypt(cyphertext []byte, key []byte) ([]byte, error) { @@ -169,3 +191,9 @@ func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { return gcm.Seal(nonce, nonce, plaintext, nil), nil } + +func shake256(buf []byte) []byte { + h := make([]byte, 64) + sha3.ShakeSum256(h, buf) + return h +} diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index 9f6131d84..f23673bda 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -11,12 +11,12 @@ import ( ) type Persistence interface { - // GetPushNotificationOptions retrieve a push notification options from storage given a public key and installation id - GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) - // DeletePushNotificationOptions deletes a push notification options from storage given a public key and installation id - DeletePushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) error - // SavePushNotificationOptions saves a push notification option to the db - SavePushNotificationOptions(publicKey *ecdsa.PublicKey, options *protobuf.PushNotificationOptions) error + // GetPushNotificationRegistration retrieve a push notification registration from storage given a public key and installation id + GetPushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) + // DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id + DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error + // SavePushNotificationRegistration saves a push notification option to the db + SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error } type SQLitePersistence struct { @@ -27,9 +27,9 @@ func NewSQLitePersistence(db *sql.DB) Persistence { return &SQLitePersistence{db: db} } -func (p *SQLitePersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationOptions, error) { - var marshaledOptions []byte - err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID).Scan(&marshaledOptions) +func (p *SQLitePersistence) GetPushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) { + var marshaledRegistration []byte + err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID).Scan(&marshaledRegistration) if err == sql.ErrNoRows { return nil, nil @@ -37,26 +37,26 @@ func (p *SQLitePersistence) GetPushNotificationOptions(publicKey *ecdsa.PublicKe return nil, err } - options := &protobuf.PushNotificationOptions{} + registration := &protobuf.PushNotificationRegistration{} - if err := proto.Unmarshal(marshaledOptions, options); err != nil { + if err := proto.Unmarshal(marshaledRegistration, registration); err != nil { return nil, err } - return options, nil + return registration, nil } -func (p *SQLitePersistence) SavePushNotificationOptions(publicKey *ecdsa.PublicKey, options *protobuf.PushNotificationOptions) error { +func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error { compressedPublicKey := crypto.CompressPubkey(publicKey) - marshaledOptions, err := proto.Marshal(options) + marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, compressedPublicKey, options.InstallationId, options.Version, marshaledOptions) + _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, compressedPublicKey, registration.InstallationId, registration.Version, marshaledRegistration) return err } -func (p *SQLitePersistence) DeletePushNotificationOptions(publicKey *ecdsa.PublicKey, installationID string) error { +func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error { _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID) return err } diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/push_notification_server_persistence_test.go index 1bd0fb10d..d0b94dc59 100644 --- a/protocol/push_notification_server/push_notification_server_persistence_test.go +++ b/protocol/push_notification_server/push_notification_server_persistence_test.go @@ -44,15 +44,15 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { s.Require().NoError(err) installationID := "54242d02-bb92-11ea-b3de-0242ac130004" - options := &protobuf.PushNotificationOptions{ + registration := &protobuf.PushNotificationRegistration{ InstallationId: installationID, Version: 5, } - s.Require().NoError(s.persistence.SavePushNotificationOptions(&key.PublicKey, options)) + s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, registration)) - retrievedOptions, err := s.persistence.GetPushNotificationOptions(&key.PublicKey, installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) s.Require().NoError(err) - s.Require().True(proto.Equal(options, retrievedOptions)) + s.Require().True(proto.Equal(registration, retrievedRegistration)) } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 9f4823584..bef6ee30a 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -54,11 +54,11 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Empty payload _, err = server.ValidateRegistration(&key.PublicKey, nil) - s.Require().Equal(ErrEmptyPushNotificationOptionsPayload, err) + s.Require().Equal(ErrEmptyPushNotificationRegistrationPayload, err) // Empty key _, err = server.ValidateRegistration(nil, []byte("payload")) - s.Require().Equal(ErrEmptyPushNotificationOptionsPublicKey, err) + s.Require().Equal(ErrEmptyPushNotificationRegistrationPublicKey, err) // Invalid cyphertext length _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) @@ -82,10 +82,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrCouldNotUnmarshalPushNotificationOptions, err) + s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err) // Missing installationID - payload, err := proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: accessToken, Version: 1, }) @@ -94,10 +94,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrMalformedPushNotificationOptionsInstallationID, err) + s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Malformed installationID - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: accessToken, InstallationId: "abc", Version: 1, @@ -105,10 +105,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrMalformedPushNotificationOptionsInstallationID, err) + s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Version set to 0 - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: accessToken, InstallationId: installationID, }) @@ -117,10 +117,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrInvalidPushNotificationOptionsVersion, err) + s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Version lower than previous one - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: accessToken, InstallationId: installationID, Version: 1, @@ -130,20 +130,20 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) - // Setup mock - s.Require().NoError(s.persistence.SavePushNotificationOptions(&key.PublicKey, &protobuf.PushNotificationOptions{ + // Setup persistence + s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, &protobuf.PushNotificationRegistration{ AccessToken: accessToken, InstallationId: installationID, Version: 2})) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrInvalidPushNotificationOptionsVersion, err) + s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) - // Cleanup mock - s.Require().NoError(s.persistence.DeletePushNotificationOptions(&key.PublicKey, installationID)) + // Cleanup persistence + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&key.PublicKey, installationID)) // Unregistering message - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ InstallationId: installationID, Unregister: true, Version: 1, @@ -156,7 +156,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { s.Require().Nil(err) // Missing access token - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ InstallationId: installationID, Version: 1, }) @@ -165,10 +165,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrMalformedPushNotificationOptionsAccessToken, err) + s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Invalid access token - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", InstallationId: installationID, Version: 1, @@ -178,10 +178,10 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrMalformedPushNotificationOptionsAccessToken, err) + s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Missing device token - payload, err = proto.Marshal(&protobuf.PushNotificationOptions{ + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: accessToken, InstallationId: installationID, Version: 1, @@ -191,5 +191,241 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = encrypt(payload, sharedKey, rand.Reader) s.Require().NoError(err) _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) - s.Require().Equal(ErrMalformedPushNotificationOptionsDeviceToken, err) + s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) + + // Successful + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + Token: "abc", + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + s.Require().NoError(err) +} + +func (s *ServerSuite) TestPushNotificationHandleRegistration() { + accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" + installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" + identity, err := crypto.GenerateKey() + s.Require().NoError(err) + + config := &Config{ + Identity: identity, + } + + server := New(config, s.persistence) + + key, err := crypto.GenerateKey() + s.Require().NoError(err) + + sharedKey, err := server.generateSharedKey(&key.PublicKey) + s.Require().NoError(err) + + // Empty payload + response := server.HandlePushNotificationRegistration(&key.PublicKey, nil) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Empty key + response = server.HandlePushNotificationRegistration(nil, []byte("payload")) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Invalid cyphertext length + response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("too short")) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Invalid cyphertext length + response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("too short")) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Invalid ciphertext + response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("not too short but invalid")) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Different key ciphertext + cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Right cyphertext but non unmarshable payload + cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Missing installationID + payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Malformed installationID + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + InstallationId: "abc", + Version: 1, + }) + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Version set to 0 + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + InstallationId: installationID, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) + + // Version lower than previous one + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + + // Setup persistence + s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, &protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 2})) + + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) + + // Cleanup persistence + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&key.PublicKey, installationID)) + + // Missing access token + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + InstallationId: installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Invalid access token + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: "bc", + InstallationId: installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Missing device token + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().False(response.Success) + s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) + + // Successful + registration := &protobuf.PushNotificationRegistration{ + Token: "abc", + AccessToken: accessToken, + InstallationId: installationID, + Version: 1, + } + payload, err = proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + // Pull from the db + retrievedRegistration, err := s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) + s.Require().NoError(err) + s.Require().NotNil(retrievedRegistration) + s.Require().True(proto.Equal(retrievedRegistration, registration)) + + // Unregistering message + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + Token: "token", + InstallationId: installationID, + Unregister: true, + Version: 2, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + s.Require().NoError(err) + response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + // Check is gone from the db + retrievedRegistration, err = s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) + s.Require().NoError(err) + s.Require().NotNil(retrievedRegistration) + s.Require().Empty(retrievedRegistration.AccessToken) + s.Require().Empty(retrievedRegistration.Token) + s.Require().Equal(uint64(2), retrievedRegistration.Version) + s.Require().Equal(installationID, retrievedRegistration.InstallationId) + s.Require().Equal(shake256(cyphertext), response.RequestId) } From 2eee956dc67e4780413051e83286d4080b7124b5 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 2 Jul 2020 12:49:04 +0200 Subject: [PATCH 11/46] Store hashes pk --- .../push_notification_server_persistence.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index f23673bda..e7d65aaa9 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -29,7 +29,7 @@ func NewSQLitePersistence(db *sql.DB) Persistence { func (p *SQLitePersistence) GetPushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) { var marshaledRegistration []byte - err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID).Scan(&marshaledRegistration) + err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, p.hashPublicKey(publicKey), installationID).Scan(&marshaledRegistration) if err == sql.ErrNoRows { return nil, nil @@ -46,17 +46,20 @@ func (p *SQLitePersistence) GetPushNotificationRegistration(publicKey *ecdsa.Pub } func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error { - compressedPublicKey := crypto.CompressPubkey(publicKey) marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, compressedPublicKey, registration.InstallationId, registration.Version, marshaledRegistration) + _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, p.hashPublicKey(publicKey), registration.InstallationId, registration.Version, marshaledRegistration) return err } func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error { - _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, crypto.CompressPubkey(publicKey), installationID) + _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, p.hashPublicKey(publicKey), installationID) return err } + +func (p *SQLitePersistence) hashPublicKey(pk *ecdsa.PublicKey) []byte { + return shake256(crypto.CompressPubkey(pk)) +} From 52da9de0c65f1dd047ed50d9d81e5ba0b75ea3ba Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 2 Jul 2020 15:57:50 +0200 Subject: [PATCH 12/46] Handle query from client --- protocol/protobuf/push_notifications.pb.go | 108 ++++--- protocol/protobuf/push_notifications.proto | 3 +- .../push_notification_server.go | 34 +- .../push_notification_server_persistence.go | 59 +++- ...sh_notification_server_persistence_test.go | 2 +- .../push_notification_server_test.go | 297 +++++++++++------- 6 files changed, 326 insertions(+), 177 deletions(-) diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 3694c30ff..603e10b94 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -377,7 +377,7 @@ type PushNotificationQueryInfo struct { AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - AllowedUserList []byte `protobuf:"bytes,4,opt,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + AllowedUserList [][]byte `protobuf:"bytes,4,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -429,7 +429,7 @@ func (m *PushNotificationQueryInfo) GetPublicKey() []byte { return nil } -func (m *PushNotificationQueryInfo) GetAllowedUserList() []byte { +func (m *PushNotificationQueryInfo) GetAllowedUserList() [][]byte { if m != nil { return m.AllowedUserList } @@ -438,6 +438,7 @@ func (m *PushNotificationQueryInfo) GetAllowedUserList() []byte { type PushNotificationQueryResponse struct { Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -475,6 +476,13 @@ func (m *PushNotificationQueryResponse) GetInfo() []*PushNotificationQueryInfo { return nil } +func (m *PushNotificationQueryResponse) GetSuccess() bool { + if m != nil { + return m.Success + } + return false +} + type PushNotification struct { AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` @@ -642,53 +650,53 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 763 bytes of a gzipped FileDescriptorProto + // 766 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xdd, 0x6e, 0xda, 0x48, - 0x14, 0x5e, 0x1b, 0x92, 0xe0, 0x13, 0x96, 0x90, 0x51, 0x7e, 0xbc, 0xd1, 0x66, 0x43, 0xbc, 0x2b, - 0x2d, 0x6a, 0x25, 0xd4, 0xa6, 0x52, 0x9b, 0x5b, 0x4a, 0x9c, 0xd6, 0x4a, 0x30, 0x74, 0x80, 0xfe, - 0x5c, 0x59, 0xc6, 0x1e, 0x82, 0x85, 0xe3, 0xa1, 0x9e, 0x71, 0x22, 0x2e, 0x2a, 0xf5, 0x09, 0xfa, - 0x26, 0xbd, 0xc9, 0x3b, 0xf4, 0xbd, 0x2a, 0x8f, 0x6d, 0x42, 0x80, 0x44, 0x5c, 0xd9, 0xe7, 0x9b, - 0xef, 0xcc, 0xcc, 0xf9, 0xce, 0xf9, 0x06, 0xd4, 0x71, 0xc4, 0x86, 0x56, 0x40, 0xb9, 0x37, 0xf0, - 0x1c, 0x9b, 0x7b, 0x34, 0x60, 0xb5, 0x71, 0x48, 0x39, 0x45, 0x05, 0xf1, 0xe9, 0x47, 0x03, 0xed, - 0x57, 0x0e, 0xfe, 0x6e, 0x47, 0x6c, 0x68, 0xce, 0xb0, 0x30, 0xb9, 0xf2, 0x18, 0x0f, 0xc5, 0x3f, - 0x6a, 0x01, 0x70, 0x3a, 0x22, 0x81, 0xc5, 0x27, 0x63, 0xa2, 0x4a, 0x15, 0xa9, 0x5a, 0x3a, 0x79, - 0x51, 0xcb, 0xf2, 0x6b, 0x4f, 0xe5, 0xd6, 0xba, 0x71, 0x62, 0x77, 0x32, 0x26, 0x58, 0xe1, 0xd9, - 0x2f, 0xda, 0x81, 0x35, 0x11, 0xa8, 0x72, 0x45, 0xaa, 0x2a, 0x38, 0x09, 0xd0, 0xff, 0xb0, 0xe5, - 0x05, 0x8c, 0xdb, 0xbe, 0x2f, 0x52, 0x2d, 0xcf, 0x55, 0x73, 0x62, 0xbd, 0x34, 0x0b, 0x1b, 0x2e, - 0x3a, 0x86, 0xa2, 0xed, 0x38, 0x84, 0x31, 0x2b, 0xd9, 0x25, 0x2f, 0x58, 0x9b, 0x09, 0x26, 0x0e, - 0x44, 0x2a, 0x6c, 0x90, 0xc0, 0xee, 0xfb, 0xc4, 0x55, 0xd7, 0x2a, 0x52, 0xb5, 0x80, 0xb3, 0x30, - 0x5e, 0xb9, 0x21, 0x21, 0xf3, 0x68, 0xa0, 0xae, 0x57, 0xa4, 0x6a, 0x1e, 0x67, 0x21, 0x7a, 0x06, - 0xdb, 0xb6, 0xef, 0xd3, 0x5b, 0xe2, 0x5a, 0x11, 0x23, 0xa1, 0xe5, 0x7b, 0x8c, 0xab, 0x1b, 0x95, - 0x5c, 0xb5, 0x88, 0xb7, 0xd2, 0x85, 0x1e, 0x23, 0xe1, 0xa5, 0xc7, 0x78, 0xcc, 0xed, 0xfb, 0xd4, - 0x19, 0x11, 0xd7, 0x72, 0x86, 0x36, 0x4f, 0xb8, 0x85, 0x84, 0x9b, 0x2e, 0x34, 0x86, 0x36, 0x17, - 0xdc, 0x7f, 0x00, 0xa2, 0x20, 0x14, 0xa2, 0x90, 0x50, 0x55, 0xc4, 0x75, 0x66, 0x10, 0xed, 0x1c, - 0x94, 0xa9, 0x4a, 0x68, 0x0f, 0x50, 0xcf, 0xbc, 0x30, 0x5b, 0x9f, 0x4c, 0xab, 0xdb, 0xba, 0xd0, - 0x4d, 0xab, 0xfb, 0xa5, 0xad, 0x97, 0xff, 0x40, 0x7f, 0x82, 0x52, 0x6f, 0xa7, 0x58, 0x59, 0x42, - 0x08, 0x4a, 0xe7, 0x06, 0xd6, 0xdf, 0xd6, 0x3b, 0x7a, 0x8a, 0xc9, 0xda, 0x9d, 0x0c, 0xff, 0x3d, - 0xd5, 0x0b, 0x4c, 0xd8, 0x98, 0x06, 0x8c, 0xc4, 0x12, 0xb0, 0x48, 0x88, 0x25, 0x9a, 0x59, 0xc0, - 0x59, 0x88, 0x4c, 0x58, 0x23, 0x61, 0x48, 0x43, 0xd1, 0x98, 0xd2, 0xc9, 0xe9, 0x6a, 0x4d, 0xce, - 0x36, 0xae, 0xe9, 0x71, 0xae, 0x68, 0x76, 0xb2, 0x0d, 0x3a, 0x04, 0x08, 0xc9, 0xd7, 0x88, 0x30, - 0x9e, 0x75, 0xb3, 0x88, 0x95, 0x14, 0x31, 0x5c, 0xed, 0xbb, 0x04, 0xca, 0x34, 0x67, 0xb6, 0x74, - 0x1d, 0xe3, 0x16, 0xce, 0x4a, 0xdf, 0x85, 0xed, 0x66, 0xfd, 0xf2, 0xbc, 0x85, 0x9b, 0xfa, 0x99, - 0xd5, 0xd4, 0x3b, 0x9d, 0xfa, 0x3b, 0xbd, 0x2c, 0xa1, 0x1d, 0x28, 0x7f, 0xd4, 0x71, 0xc7, 0x68, - 0x99, 0x56, 0xd3, 0xe8, 0x34, 0xeb, 0xdd, 0xc6, 0xfb, 0xb2, 0x8c, 0x0e, 0x60, 0xaf, 0x67, 0x76, - 0x7a, 0xed, 0x76, 0x0b, 0x77, 0xf5, 0xb3, 0x59, 0x0d, 0x73, 0xb1, 0x68, 0x86, 0xd9, 0xd5, 0xb1, - 0x59, 0xbf, 0x4c, 0x4e, 0x28, 0xe7, 0xb5, 0x1f, 0x12, 0x1c, 0xcf, 0xd7, 0x56, 0x77, 0x6f, 0x48, - 0xc8, 0x3d, 0x46, 0xae, 0x49, 0xc0, 0x8d, 0x60, 0x40, 0xe3, 0x3a, 0xc6, 0x51, 0xdf, 0xf7, 0x1c, - 0x6b, 0x44, 0x26, 0x42, 0xb4, 0x22, 0x56, 0x12, 0xe4, 0x82, 0x4c, 0x16, 0x06, 0x52, 0x5e, 0x1c, - 0xc8, 0x55, 0x87, 0x5b, 0xfb, 0x06, 0x6a, 0x83, 0x06, 0xdc, 0x76, 0x78, 0x83, 0xba, 0xe4, 0xc1, - 0x55, 0x90, 0x0d, 0x7b, 0x0b, 0x7e, 0xb6, 0xbc, 0x60, 0x40, 0x55, 0xa9, 0x92, 0xab, 0x6e, 0x9e, - 0x3c, 0x7f, 0xbc, 0x5f, 0x0b, 0x35, 0xe1, 0x9d, 0xf1, 0x1c, 0x25, 0x46, 0xb5, 0x53, 0xd8, 0x9d, - 0x4f, 0xfd, 0x10, 0x91, 0x70, 0x82, 0x8e, 0x60, 0xf3, 0x5e, 0x02, 0x26, 0x0e, 0x2c, 0x62, 0x98, - 0x6a, 0xc0, 0xb4, 0x3b, 0x09, 0xfe, 0x5a, 0x9a, 0x2a, 0x14, 0x9c, 0x97, 0x48, 0x5a, 0x49, 0x22, - 0x79, 0xa9, 0xff, 0x1f, 0x76, 0x23, 0x37, 0xdf, 0x8d, 0xa5, 0x3e, 0xce, 0x0b, 0xd6, 0xbc, 0x8f, - 0xb5, 0xcf, 0x70, 0xb8, 0xf4, 0xce, 0x53, 0xaf, 0xbc, 0x81, 0xfc, 0x8c, 0xc0, 0xff, 0x3e, 0x2e, - 0xf0, 0xb4, 0x54, 0x2c, 0x12, 0x34, 0x13, 0xca, 0xf3, 0x94, 0x55, 0x44, 0xd8, 0x87, 0x0d, 0xf1, - 0xa0, 0x4c, 0x8b, 0x5f, 0x8f, 0x43, 0xc3, 0xd5, 0x7e, 0x4a, 0xb0, 0xbf, 0x68, 0x42, 0xe1, 0x24, - 0xf4, 0x1a, 0x0a, 0xa9, 0xa9, 0x58, 0x7a, 0xd1, 0x83, 0x27, 0x9c, 0x3b, 0xe5, 0xc6, 0x0f, 0xc1, - 0x35, 0x61, 0xcc, 0xbe, 0x22, 0xe2, 0xb0, 0x22, 0xce, 0xc2, 0x58, 0xe2, 0xf4, 0xf7, 0x7e, 0x52, - 0x95, 0x14, 0xc9, 0x5e, 0xe0, 0x91, 0x15, 0x6f, 0xe4, 0x85, 0xc4, 0xbd, 0x7f, 0x81, 0x47, 0x38, - 0x85, 0xb4, 0x97, 0x70, 0xb4, 0x30, 0x83, 0xce, 0x28, 0xa0, 0xb7, 0x3e, 0x71, 0xaf, 0x92, 0x71, - 0x2e, 0x81, 0xec, 0xb9, 0xa9, 0x08, 0xb2, 0xe7, 0xf6, 0xd7, 0xc5, 0x9d, 0x5f, 0xfd, 0x0e, 0x00, - 0x00, 0xff, 0xff, 0x6a, 0xc9, 0xcf, 0x6e, 0xb5, 0x06, 0x00, 0x00, + 0x14, 0x5e, 0x1b, 0x92, 0xe0, 0x13, 0x96, 0x90, 0x51, 0x7e, 0xbc, 0xd1, 0x66, 0x43, 0xbc, 0x95, + 0x8a, 0x5a, 0x09, 0xb5, 0xa9, 0xd4, 0xe6, 0x96, 0x12, 0xa7, 0xb5, 0x12, 0x0c, 0x1d, 0xa0, 0x55, + 0xaf, 0x2c, 0x63, 0x0f, 0xc1, 0xc2, 0xb1, 0xe9, 0xcc, 0x38, 0x11, 0x17, 0x95, 0xfa, 0x04, 0x7d, + 0x93, 0xde, 0xe4, 0x1d, 0xfa, 0x5e, 0x95, 0xc7, 0x36, 0x21, 0x40, 0x22, 0xae, 0xec, 0xf3, 0xcd, + 0xf9, 0x66, 0xe6, 0x7c, 0xe7, 0x7c, 0x03, 0xea, 0x38, 0x62, 0x43, 0x2b, 0x08, 0xb9, 0x37, 0xf0, + 0x1c, 0x9b, 0x7b, 0x61, 0xc0, 0x6a, 0x63, 0x1a, 0xf2, 0x10, 0x15, 0xc4, 0xa7, 0x1f, 0x0d, 0xb4, + 0xdf, 0x39, 0xf8, 0xb7, 0x1d, 0xb1, 0xa1, 0x39, 0x93, 0x85, 0xc9, 0x95, 0xc7, 0x38, 0x15, 0xff, + 0xa8, 0x05, 0xc0, 0xc3, 0x11, 0x09, 0x2c, 0x3e, 0x19, 0x13, 0x55, 0xaa, 0x48, 0xd5, 0xd2, 0xc9, + 0xab, 0x5a, 0xc6, 0xaf, 0x3d, 0xc5, 0xad, 0x75, 0x63, 0x62, 0x77, 0x32, 0x26, 0x58, 0xe1, 0xd9, + 0x2f, 0xda, 0x81, 0x35, 0x11, 0xa8, 0x72, 0x45, 0xaa, 0x2a, 0x38, 0x09, 0xd0, 0x73, 0xd8, 0xf2, + 0x02, 0xc6, 0x6d, 0xdf, 0x17, 0x54, 0xcb, 0x73, 0xd5, 0x9c, 0x58, 0x2f, 0xcd, 0xc2, 0x86, 0x8b, + 0x8e, 0xa1, 0x68, 0x3b, 0x0e, 0x61, 0xcc, 0x4a, 0x76, 0xc9, 0x8b, 0xac, 0xcd, 0x04, 0x13, 0x07, + 0x22, 0x15, 0x36, 0x48, 0x60, 0xf7, 0x7d, 0xe2, 0xaa, 0x6b, 0x15, 0xa9, 0x5a, 0xc0, 0x59, 0x18, + 0xaf, 0xdc, 0x10, 0xca, 0xbc, 0x30, 0x50, 0xd7, 0x2b, 0x52, 0x35, 0x8f, 0xb3, 0x10, 0xbd, 0x80, + 0x6d, 0xdb, 0xf7, 0xc3, 0x5b, 0xe2, 0x5a, 0x11, 0x23, 0xd4, 0xf2, 0x3d, 0xc6, 0xd5, 0x8d, 0x4a, + 0xae, 0x5a, 0xc4, 0x5b, 0xe9, 0x42, 0x8f, 0x11, 0x7a, 0xe9, 0x31, 0x1e, 0xe7, 0xf6, 0xfd, 0xd0, + 0x19, 0x11, 0xd7, 0x72, 0x86, 0x36, 0x4f, 0x72, 0x0b, 0x49, 0x6e, 0xba, 0xd0, 0x18, 0xda, 0x5c, + 0xe4, 0xfe, 0x07, 0x10, 0x05, 0x54, 0x88, 0x42, 0xa8, 0xaa, 0x88, 0xeb, 0xcc, 0x20, 0xda, 0x39, + 0x28, 0x53, 0x95, 0xd0, 0x1e, 0xa0, 0x9e, 0x79, 0x61, 0xb6, 0xbe, 0x98, 0x56, 0xb7, 0x75, 0xa1, + 0x9b, 0x56, 0xf7, 0x6b, 0x5b, 0x2f, 0xff, 0x85, 0xfe, 0x06, 0xa5, 0xde, 0x4e, 0xb1, 0xb2, 0x84, + 0x10, 0x94, 0xce, 0x0d, 0xac, 0xbf, 0xaf, 0x77, 0xf4, 0x14, 0x93, 0xb5, 0x3b, 0x19, 0x9e, 0x3d, + 0xd5, 0x0b, 0x4c, 0xd8, 0x38, 0x0c, 0x18, 0x89, 0x25, 0x60, 0x91, 0x10, 0x4b, 0x34, 0xb3, 0x80, + 0xb3, 0x10, 0x99, 0xb0, 0x46, 0x28, 0x0d, 0xa9, 0x68, 0x4c, 0xe9, 0xe4, 0x74, 0xb5, 0x26, 0x67, + 0x1b, 0xd7, 0xf4, 0x98, 0x2b, 0x9a, 0x9d, 0x6c, 0x83, 0x0e, 0x01, 0x28, 0xf9, 0x16, 0x11, 0xc6, + 0xb3, 0x6e, 0x16, 0xb1, 0x92, 0x22, 0x86, 0xab, 0xfd, 0x90, 0x40, 0x99, 0x72, 0x66, 0x4b, 0xd7, + 0x31, 0x6e, 0xe1, 0xac, 0xf4, 0x5d, 0xd8, 0x6e, 0xd6, 0x2f, 0xcf, 0x5b, 0xb8, 0xa9, 0x9f, 0x59, + 0x4d, 0xbd, 0xd3, 0xa9, 0x7f, 0xd0, 0xcb, 0x12, 0xda, 0x81, 0xf2, 0x67, 0x1d, 0x77, 0x8c, 0x96, + 0x69, 0x35, 0x8d, 0x4e, 0xb3, 0xde, 0x6d, 0x7c, 0x2c, 0xcb, 0xe8, 0x00, 0xf6, 0x7a, 0x66, 0xa7, + 0xd7, 0x6e, 0xb7, 0x70, 0x57, 0x3f, 0x9b, 0xd5, 0x30, 0x17, 0x8b, 0x66, 0x98, 0x5d, 0x1d, 0x9b, + 0xf5, 0xcb, 0xe4, 0x84, 0x72, 0x5e, 0xfb, 0x29, 0xc1, 0xf1, 0x7c, 0x6d, 0x75, 0xf7, 0x86, 0x50, + 0xee, 0x31, 0x72, 0x4d, 0x02, 0x6e, 0x04, 0x83, 0x30, 0xae, 0x63, 0x1c, 0xf5, 0x7d, 0xcf, 0xb1, + 0x46, 0x64, 0x22, 0x44, 0x2b, 0x62, 0x25, 0x41, 0x2e, 0xc8, 0x64, 0x61, 0x20, 0xe5, 0xc5, 0x81, + 0x5c, 0x75, 0xb8, 0xb5, 0xef, 0xa0, 0x36, 0xc2, 0x80, 0xdb, 0x0e, 0x6f, 0x84, 0x2e, 0x79, 0x70, + 0x15, 0x64, 0xc3, 0xde, 0x82, 0x9f, 0x2d, 0x2f, 0x18, 0x84, 0xaa, 0x54, 0xc9, 0x55, 0x37, 0x4f, + 0x5e, 0x3e, 0xde, 0xaf, 0x85, 0x9a, 0xf0, 0xce, 0x78, 0x2e, 0x25, 0x46, 0xb5, 0x53, 0xd8, 0x9d, + 0xa7, 0x7e, 0x8a, 0x08, 0x9d, 0xa0, 0x23, 0xd8, 0xbc, 0x97, 0x80, 0x89, 0x03, 0x8b, 0x18, 0xa6, + 0x1a, 0x30, 0xed, 0x4e, 0x82, 0x7f, 0x96, 0x52, 0x85, 0x82, 0xf3, 0x12, 0x49, 0x2b, 0x49, 0x24, + 0x2f, 0xf5, 0xff, 0xc3, 0x6e, 0xe4, 0xe6, 0xbb, 0xb1, 0xd4, 0xc7, 0xf9, 0xa5, 0x3e, 0xd6, 0x28, + 0x1c, 0x2e, 0xbd, 0xf3, 0xd4, 0x2b, 0xef, 0x20, 0x3f, 0x23, 0xf0, 0xff, 0x8f, 0x0b, 0x3c, 0x2d, + 0x15, 0x0b, 0xc2, 0xac, 0xc9, 0xe4, 0x07, 0x26, 0xd3, 0x4c, 0x28, 0xcf, 0x93, 0x57, 0x91, 0x67, + 0x1f, 0x36, 0xc4, 0x53, 0x33, 0x95, 0x65, 0x3d, 0x0e, 0x0d, 0x57, 0xfb, 0x25, 0xc1, 0xfe, 0xa2, + 0x3d, 0x85, 0xc7, 0xd0, 0x5b, 0x28, 0xa4, 0x76, 0x63, 0x69, 0x09, 0x07, 0x4f, 0x78, 0x7a, 0x9a, + 0x1b, 0xdf, 0xfe, 0x9a, 0x30, 0x66, 0x5f, 0x11, 0x71, 0x58, 0x11, 0x67, 0x61, 0x2c, 0x7e, 0xfa, + 0x7b, 0x3f, 0xc3, 0x4a, 0x8a, 0x64, 0x6f, 0xf3, 0xc8, 0x8a, 0x37, 0xf2, 0x28, 0x71, 0xef, 0xdf, + 0xe6, 0x11, 0x4e, 0x21, 0xed, 0x35, 0x1c, 0x2d, 0x4c, 0xa7, 0x33, 0x0a, 0xc2, 0x5b, 0x9f, 0xb8, + 0x57, 0xc9, 0xa0, 0x97, 0x40, 0xf6, 0xdc, 0x54, 0x04, 0xd9, 0x73, 0xfb, 0xeb, 0xe2, 0xce, 0x6f, + 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x16, 0x49, 0x40, 0xfd, 0xcf, 0x06, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index 21f11353c..f3b5bc9a2 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -51,11 +51,12 @@ message PushNotificationQueryInfo { string access_token = 1; string installation_id = 2; bytes public_key = 3; - bytes allowed_user_list = 4; + repeated bytes allowed_user_list = 4; } message PushNotificationQueryResponse { repeated PushNotificationQueryInfo info = 1; + bool success = 2; } message PushNotification { diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index ed809d6ec..75d02c766 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -90,7 +90,7 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrMalformedPushNotificationRegistrationInstallationID } - previousRegistration, err := p.persistence.GetPushNotificationRegistration(publicKey, registration.InstallationId) + previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey, registration.InstallationId) if err != nil { return nil, err } @@ -115,6 +115,38 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return registration, nil } +func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { + response := &protobuf.PushNotificationQueryResponse{} + if query == nil || len(query.PublicKeys) == 0 { + return response + } + + registrations, err := p.persistence.GetPushNotificationRegistrationByPublicKeys(query.PublicKeys) + if err != nil { + // TODO: log errors + return response + } + + for _, idAndResponse := range registrations { + + registration := idAndResponse.Registration + info := &protobuf.PushNotificationQueryInfo{ + PublicKey: idAndResponse.ID, + InstallationId: registration.InstallationId, + } + + if len(registration.AllowedUserList) > 0 { + info.AllowedUserList = registration.AllowedUserList + } else { + info.AccessToken = registration.AccessToken + } + response.Info = append(response.Info, info) + } + + response.Success = true + return response +} + func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { response := &protobuf.PushNotificationRegistrationResponse{ RequestId: shake256(payload), diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index e7d65aaa9..6213b70ff 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -3,6 +3,7 @@ package push_notification_server import ( "crypto/ecdsa" "database/sql" + "strings" "github.com/golang/protobuf/proto" @@ -11,8 +12,11 @@ import ( ) type Persistence interface { - // GetPushNotificationRegistration retrieve a push notification registration from storage given a public key and installation id - GetPushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) + // GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id + GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) + // GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key + GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) + // DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error // SavePushNotificationRegistration saves a push notification option to the db @@ -27,9 +31,9 @@ func NewSQLitePersistence(db *sql.DB) Persistence { return &SQLitePersistence{db: db} } -func (p *SQLitePersistence) GetPushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) { +func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) { var marshaledRegistration []byte - err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, p.hashPublicKey(publicKey), installationID).Scan(&marshaledRegistration) + err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, hashPublicKey(publicKey), installationID).Scan(&marshaledRegistration) if err == sql.ErrNoRows { return nil, nil @@ -45,21 +49,62 @@ func (p *SQLitePersistence) GetPushNotificationRegistration(publicKey *ecdsa.Pub return registration, nil } +type PushNotificationIDAndRegistration struct { + ID []byte + Registration *protobuf.PushNotificationRegistration +} + +func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) { + // TODO: check for a max number of keys + + publicKeyArgs := make([]interface{}, 0, len(publicKeys)) + for _, pk := range publicKeys { + publicKeyArgs = append(publicKeyArgs, pk) + } + + inVector := strings.Repeat("?, ", len(publicKeys)-1) + "?" + + rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE public_key IN (`+inVector+`)`, publicKeyArgs...) + if err != nil { + return nil, err + } + defer rows.Close() + + var registrations []*PushNotificationIDAndRegistration + for rows.Next() { + response := &PushNotificationIDAndRegistration{} + var marshaledRegistration []byte + err := rows.Scan(&response.ID, &marshaledRegistration) + if err != nil { + return nil, err + } + + registration := &protobuf.PushNotificationRegistration{} + + if err := proto.Unmarshal(marshaledRegistration, registration); err != nil { + return nil, err + } + response.Registration = registration + registrations = append(registrations, response) + } + return registrations, nil +} + func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error { marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, p.hashPublicKey(publicKey), registration.InstallationId, registration.Version, marshaledRegistration) + _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, hashPublicKey(publicKey), registration.InstallationId, registration.Version, marshaledRegistration) return err } func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error { - _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, p.hashPublicKey(publicKey), installationID) + _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, hashPublicKey(publicKey), installationID) return err } -func (p *SQLitePersistence) hashPublicKey(pk *ecdsa.PublicKey) []byte { +func hashPublicKey(pk *ecdsa.PublicKey) []byte { return shake256(crypto.CompressPubkey(pk)) } diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/push_notification_server_persistence_test.go index d0b94dc59..bc0ec7052 100644 --- a/protocol/push_notification_server/push_notification_server_persistence_test.go +++ b/protocol/push_notification_server/push_notification_server_persistence_test.go @@ -51,7 +51,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, registration)) - retrievedRegistration, err := s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&key.PublicKey, installationID) s.Require().NoError(err) s.Require().True(proto.Equal(registration, retrievedRegistration)) diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index bef6ee30a..081849c78 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -1,6 +1,7 @@ package push_notification_server import ( + "crypto/ecdsa" "crypto/rand" "io/ioutil" "os" @@ -15,13 +16,23 @@ import ( ) func TestServerSuite(t *testing.T) { - suite.Run(t, new(ServerSuite)) + s := new(ServerSuite) + s.accessToken = "b6ae4fde-bb65-11ea-b3de-0242ac130004" + s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004" + + suite.Run(t, s) } type ServerSuite struct { suite.Suite - tmpFile *os.File - persistence Persistence + tmpFile *os.File + persistence Persistence + accessToken string + installationID string + identity *ecdsa.PrivateKey + key *ecdsa.PrivateKey + sharedKey []byte + server *Server } func (s *ServerSuite) SetupTest() { @@ -32,226 +43,212 @@ func (s *ServerSuite) SetupTest() { database, err := sqlite.Open(s.tmpFile.Name(), "") s.Require().NoError(err) s.persistence = NewSQLitePersistence(database) -} -func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { - accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" - installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" identity, err := crypto.GenerateKey() s.Require().NoError(err) + s.identity = identity + + key, err := crypto.GenerateKey() + s.Require().NoError(err) + s.key = key config := &Config{ Identity: identity, } - server := New(config, s.persistence) + s.server = New(config, s.persistence) - key, err := crypto.GenerateKey() + sharedKey, err := s.server.generateSharedKey(&s.key.PublicKey) s.Require().NoError(err) + s.sharedKey = sharedKey - sharedKey, err := server.generateSharedKey(&key.PublicKey) - s.Require().NoError(err) +} + +func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Empty payload - _, err = server.ValidateRegistration(&key.PublicKey, nil) + _, err := s.server.ValidateRegistration(&s.key.PublicKey, nil) s.Require().Equal(ErrEmptyPushNotificationRegistrationPayload, err) // Empty key - _, err = server.ValidateRegistration(nil, []byte("payload")) + _, err = s.server.ValidateRegistration(nil, []byte("payload")) s.Require().Equal(ErrEmptyPushNotificationRegistrationPublicKey, err) // Invalid cyphertext length - _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) s.Require().Equal(ErrInvalidCiphertextLength, err) // Invalid cyphertext length - _, err = server.ValidateRegistration(&key.PublicKey, []byte("too short")) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) s.Require().Equal(ErrInvalidCiphertextLength, err) // Invalid ciphertext - _, err = server.ValidateRegistration(&key.PublicKey, []byte("not too short but invalid")) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("not too short but invalid")) s.Require().Error(ErrInvalidCiphertextLength, err) // Different key ciphertext cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Error(err) // Right cyphertext but non unmarshable payload - cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) + cyphertext, err = encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err) // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, + AccessToken: s.accessToken, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Malformed installationID payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, + AccessToken: s.accessToken, InstallationId: "abc", Version: 1, }) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, &protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + s.Require().NoError(s.persistence.SavePushNotificationRegistration(&s.key.PublicKey, &protobuf.PushNotificationRegistration{ + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 2})) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&key.PublicKey, installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&s.key.PublicKey, s.installationID)) // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - InstallationId: installationID, + InstallationId: s.installationID, Unregister: true, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Nil(err) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - InstallationId: installationID, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", - InstallationId: installationID, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) // Successful payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ Token: "abc", - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = server.ValidateRegistration(&key.PublicKey, cyphertext) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().NoError(err) } func (s *ServerSuite) TestPushNotificationHandleRegistration() { - accessToken := "b6ae4fde-bb65-11ea-b3de-0242ac130004" - installationID := "c6ae4fde-bb65-11ea-b3de-0242ac130004" - identity, err := crypto.GenerateKey() - s.Require().NoError(err) - - config := &Config{ - Identity: identity, - } - - server := New(config, s.persistence) - - key, err := crypto.GenerateKey() - s.Require().NoError(err) - - sharedKey, err := server.generateSharedKey(&key.PublicKey) - s.Require().NoError(err) - // Empty payload - response := server.HandlePushNotificationRegistration(&key.PublicKey, nil) + response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, nil) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Empty key - response = server.HandlePushNotificationRegistration(nil, []byte("payload")) + response = s.server.HandlePushNotificationRegistration(nil, []byte("payload")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid cyphertext length - response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("too short")) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("too short")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid cyphertext length - response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("too short")) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("too short")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid ciphertext - response = server.HandlePushNotificationRegistration(&key.PublicKey, []byte("not too short but invalid")) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("not too short but invalid")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -259,95 +256,95 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Different key ciphertext cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Right cyphertext but non unmarshable payload - cyphertext, err = encrypt([]byte("plaintext"), sharedKey, rand.Reader) + cyphertext, err = encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, + AccessToken: s.accessToken, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Malformed installationID payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, + AccessToken: s.accessToken, InstallationId: "abc", Version: 1, }) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, &protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + s.Require().NoError(s.persistence.SavePushNotificationRegistration(&s.key.PublicKey, &protobuf.PushNotificationRegistration{ + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 2})) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&key.PublicKey, installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&s.key.PublicKey, s.installationID)) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - InstallationId: installationID, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -355,29 +352,29 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", - InstallationId: installationID, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -385,21 +382,21 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Successful registration := &protobuf.PushNotificationRegistration{ Token: "abc", - AccessToken: accessToken, - InstallationId: installationID, + AccessToken: s.accessToken, + InstallationId: s.installationID, Version: 1, } payload, err = proto.Marshal(registration) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) // Pull from the db - retrievedRegistration, err := s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&s.key.PublicKey, s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().True(proto.Equal(retrievedRegistration, registration)) @@ -407,25 +404,91 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ Token: "token", - InstallationId: installationID, + InstallationId: s.installationID, Unregister: true, Version: 2, }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, sharedKey, rand.Reader) + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = server.HandlePushNotificationRegistration(&key.PublicKey, cyphertext) + response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) // Check is gone from the db - retrievedRegistration, err = s.persistence.GetPushNotificationRegistration(&key.PublicKey, installationID) + retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&s.key.PublicKey, s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().Empty(retrievedRegistration.AccessToken) s.Require().Empty(retrievedRegistration.Token) s.Require().Equal(uint64(2), retrievedRegistration.Version) - s.Require().Equal(installationID, retrievedRegistration.InstallationId) + s.Require().Equal(s.installationID, retrievedRegistration.InstallationId) s.Require().Equal(shake256(cyphertext), response.RequestId) } + +func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { + hashedPublicKey := hashPublicKey(&s.key.PublicKey) + // Successful + registration := &protobuf.PushNotificationRegistration{ + Token: "abc", + AccessToken: s.accessToken, + InstallationId: s.installationID, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + query := &protobuf.PushNotificationQuery{ + PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey}, + } + + queryResponse := s.server.HandlePushNotificationQuery(query) + s.Require().NotNil(queryResponse) + s.Require().True(queryResponse.Success) + s.Require().Len(queryResponse.Info, 1) + s.Require().Equal(s.accessToken, queryResponse.Info[0].AccessToken) + s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey) + s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) + s.Require().Nil(queryResponse.Info[0].AllowedUserList) +} + +func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { + hashedPublicKey := hashPublicKey(&s.key.PublicKey) + allowedUserList := [][]byte{[]byte("a")} + // Successful + + registration := &protobuf.PushNotificationRegistration{ + Token: "abc", + AccessToken: s.accessToken, + InstallationId: s.installationID, + AllowedUserList: allowedUserList, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + query := &protobuf.PushNotificationQuery{ + PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey}, + } + + queryResponse := s.server.HandlePushNotificationQuery(query) + s.Require().NotNil(queryResponse) + s.Require().True(queryResponse.Success) + s.Require().Len(queryResponse.Info, 1) + s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey) + s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) + s.Require().Equal(allowedUserList, queryResponse.Info[0].AllowedUserList) +} From 6ed7f705b92218ce78e6deb8dc72f260b6a3f4df Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 2 Jul 2020 16:19:21 +0200 Subject: [PATCH 13/46] validate token type --- protocol/push_notification_server/errors.go | 3 ++- .../push_notification_server.go | 4 +++ .../push_notification_server_test.go | 27 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/protocol/push_notification_server/errors.go b/protocol/push_notification_server/errors.go index 69b24a837..176215483 100644 --- a/protocol/push_notification_server/errors.go +++ b/protocol/push_notification_server/errors.go @@ -8,5 +8,6 @@ var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key") var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences") var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") -var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token") var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token") +var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token") +var ErrUnknownPushNotificationRegistrationTokenType = errors.New("invalid token type") diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 75d02c766..2d386d1a6 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -112,6 +112,10 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrMalformedPushNotificationRegistrationDeviceToken } + if registration.TokenType == protobuf.PushNotificationRegistration_UNKNOWN_TOKEN_TYPE { + return nil, ErrUnknownPushNotificationRegistrationTokenType + } + return registration, nil } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 081849c78..8f5d6e8b7 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -101,6 +101,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, Version: 1, }) s.Require().NoError(err) @@ -113,6 +114,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Malformed installationID payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: "abc", Version: 1, }) @@ -124,6 +126,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, }) s.Require().NoError(err) @@ -136,6 +139,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, }) @@ -147,6 +151,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Setup persistence s.Require().NoError(s.persistence.SavePushNotificationRegistration(&s.key.PublicKey, &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 2})) @@ -158,6 +163,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Unregister: true, Version: 1, @@ -172,6 +178,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ InstallationId: s.installationID, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, Version: 1, }) s.Require().NoError(err) @@ -184,6 +191,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, }) @@ -197,6 +205,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, }) @@ -207,10 +216,25 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) + // Missing token type + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: s.accessToken, + Token: "device-token", + InstallationId: s.installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + s.Require().Equal(ErrUnknownPushNotificationRegistrationTokenType, err) + // Successful payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, }) @@ -383,6 +407,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, } @@ -433,6 +458,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, } @@ -467,6 +493,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, AllowedUserList: allowedUserList, Version: 1, From 927f7625890c04a08a389e9d848a6c917a5f9553 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 3 Jul 2020 10:02:28 +0200 Subject: [PATCH 14/46] Send gorush notifications --- protocol/protobuf/push_notifications.pb.go | 289 +++++++++++++----- protocol/protobuf/push_notifications.proto | 26 +- protocol/push_notification_server/common.go | 58 ++++ protocol/push_notification_server/gorush.go | 79 +++++ .../push_notification_server/gorush_test.go | 107 +++++++ .../push_notification_server.go | 109 +++---- .../push_notification_server_persistence.go | 24 +- ...sh_notification_server_persistence_test.go | 4 +- .../push_notification_server_test.go | 12 +- 9 files changed, 545 insertions(+), 163 deletions(-) create mode 100644 protocol/push_notification_server/common.go create mode 100644 protocol/push_notification_server/gorush.go create mode 100644 protocol/push_notification_server/gorush_test.go diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 603e10b94..f853da320 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -82,6 +82,37 @@ func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, return fileDescriptor_200acd86044eaa5d, []int{1, 0} } +type PushNotificationReport_ErrorType int32 + +const ( + PushNotificationReport_UNKNOWN_ERROR_TYPE PushNotificationReport_ErrorType = 0 + PushNotificationReport_WRONG_TOKEN PushNotificationReport_ErrorType = 1 + PushNotificationReport_INTERNAL_ERROR PushNotificationReport_ErrorType = 2 + PushNotificationReport_NOT_REGISTERED PushNotificationReport_ErrorType = 3 +) + +var PushNotificationReport_ErrorType_name = map[int32]string{ + 0: "UNKNOWN_ERROR_TYPE", + 1: "WRONG_TOKEN", + 2: "INTERNAL_ERROR", + 3: "NOT_REGISTERED", +} + +var PushNotificationReport_ErrorType_value = map[string]int32{ + "UNKNOWN_ERROR_TYPE": 0, + "WRONG_TOKEN": 1, + "INTERNAL_ERROR": 2, + "NOT_REGISTERED": 3, +} + +func (x PushNotificationReport_ErrorType) String() string { + return proto.EnumName(PushNotificationReport_ErrorType_name, int32(x)) +} + +func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{9, 0} +} + type PushNotificationRegistration struct { TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` @@ -486,6 +517,9 @@ func (m *PushNotificationQueryResponse) GetSuccess() bool { type PushNotification struct { AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -530,11 +564,31 @@ func (m *PushNotification) GetChatId() string { return "" } +func (m *PushNotification) GetPublicKey() []byte { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *PushNotification) GetInstallationId() string { + if m != nil { + return m.InstallationId + } + return "" +} + +func (m *PushNotification) GetMessage() []byte { + if m != nil { + return m.Message + } + return nil +} + type PushNotificationRequest struct { Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"` - Message []byte `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` - MessageId string `protobuf:"bytes,3,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` - AckRequired string `protobuf:"bytes,4,opt,name=ack_required,json=ackRequired,proto3" json:"ack_required,omitempty"` + MessageId string `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + AckRequired string `protobuf:"bytes,3,opt,name=ack_required,json=ackRequired,proto3" json:"ack_required,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -572,13 +626,6 @@ func (m *PushNotificationRequest) GetRequests() []*PushNotification { return nil } -func (m *PushNotificationRequest) GetMessage() []byte { - if m != nil { - return m.Message - } - return nil -} - func (m *PushNotificationRequest) GetMessageId() string { if m != nil { return m.MessageId @@ -593,48 +640,120 @@ func (m *PushNotificationRequest) GetAckRequired() string { return "" } -type PushNotificationAcknowledgement struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type PushNotificationReport struct { + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Error PushNotificationReport_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationReport_ErrorType" json:"error,omitempty"` + PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *PushNotificationAcknowledgement) Reset() { *m = PushNotificationAcknowledgement{} } -func (m *PushNotificationAcknowledgement) String() string { return proto.CompactTextString(m) } -func (*PushNotificationAcknowledgement) ProtoMessage() {} -func (*PushNotificationAcknowledgement) Descriptor() ([]byte, []int) { +func (m *PushNotificationReport) Reset() { *m = PushNotificationReport{} } +func (m *PushNotificationReport) String() string { return proto.CompactTextString(m) } +func (*PushNotificationReport) ProtoMessage() {} +func (*PushNotificationReport) Descriptor() ([]byte, []int) { return fileDescriptor_200acd86044eaa5d, []int{9} } -func (m *PushNotificationAcknowledgement) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationAcknowledgement.Unmarshal(m, b) +func (m *PushNotificationReport) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationReport.Unmarshal(m, b) } -func (m *PushNotificationAcknowledgement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationAcknowledgement.Marshal(b, m, deterministic) +func (m *PushNotificationReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationReport.Marshal(b, m, deterministic) } -func (m *PushNotificationAcknowledgement) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationAcknowledgement.Merge(m, src) +func (m *PushNotificationReport) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationReport.Merge(m, src) } -func (m *PushNotificationAcknowledgement) XXX_Size() int { - return xxx_messageInfo_PushNotificationAcknowledgement.Size(m) +func (m *PushNotificationReport) XXX_Size() int { + return xxx_messageInfo_PushNotificationReport.Size(m) } -func (m *PushNotificationAcknowledgement) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationAcknowledgement.DiscardUnknown(m) +func (m *PushNotificationReport) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationReport.DiscardUnknown(m) } -var xxx_messageInfo_PushNotificationAcknowledgement proto.InternalMessageInfo +var xxx_messageInfo_PushNotificationReport proto.InternalMessageInfo -func (m *PushNotificationAcknowledgement) GetId() string { +func (m *PushNotificationReport) GetSuccess() bool { if m != nil { - return m.Id + return m.Success + } + return false +} + +func (m *PushNotificationReport) GetError() PushNotificationReport_ErrorType { + if m != nil { + return m.Error + } + return PushNotificationReport_UNKNOWN_ERROR_TYPE +} + +func (m *PushNotificationReport) GetPublicKey() []byte { + if m != nil { + return m.PublicKey + } + return nil +} + +func (m *PushNotificationReport) GetInstallationId() string { + if m != nil { + return m.InstallationId } return "" } +type PushNotificationResponse struct { + MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + Reports []*PushNotificationReport `protobuf:"bytes,2,rep,name=reports,proto3" json:"reports,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PushNotificationResponse) Reset() { *m = PushNotificationResponse{} } +func (m *PushNotificationResponse) String() string { return proto.CompactTextString(m) } +func (*PushNotificationResponse) ProtoMessage() {} +func (*PushNotificationResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_200acd86044eaa5d, []int{10} +} + +func (m *PushNotificationResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_PushNotificationResponse.Unmarshal(m, b) +} +func (m *PushNotificationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_PushNotificationResponse.Marshal(b, m, deterministic) +} +func (m *PushNotificationResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PushNotificationResponse.Merge(m, src) +} +func (m *PushNotificationResponse) XXX_Size() int { + return xxx_messageInfo_PushNotificationResponse.Size(m) +} +func (m *PushNotificationResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PushNotificationResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PushNotificationResponse proto.InternalMessageInfo + +func (m *PushNotificationResponse) GetMessageId() string { + if m != nil { + return m.MessageId + } + return "" +} + +func (m *PushNotificationResponse) GetReports() []*PushNotificationReport { + if m != nil { + return m.Reports + } + return nil +} + func init() { proto.RegisterEnum("protobuf.PushNotificationRegistration_TokenType", PushNotificationRegistration_TokenType_name, PushNotificationRegistration_TokenType_value) proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value) + proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value) proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration") proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") @@ -644,59 +763,65 @@ func init() { proto.RegisterType((*PushNotificationQueryResponse)(nil), "protobuf.PushNotificationQueryResponse") proto.RegisterType((*PushNotification)(nil), "protobuf.PushNotification") proto.RegisterType((*PushNotificationRequest)(nil), "protobuf.PushNotificationRequest") - proto.RegisterType((*PushNotificationAcknowledgement)(nil), "protobuf.PushNotificationAcknowledgement") + proto.RegisterType((*PushNotificationReport)(nil), "protobuf.PushNotificationReport") + proto.RegisterType((*PushNotificationResponse)(nil), "protobuf.PushNotificationResponse") } func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 766 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xdd, 0x6e, 0xda, 0x48, - 0x14, 0x5e, 0x1b, 0x92, 0xe0, 0x13, 0x96, 0x90, 0x51, 0x7e, 0xbc, 0xd1, 0x66, 0x43, 0xbc, 0x95, - 0x8a, 0x5a, 0x09, 0xb5, 0xa9, 0xd4, 0xe6, 0x96, 0x12, 0xa7, 0xb5, 0x12, 0x0c, 0x1d, 0xa0, 0x55, - 0xaf, 0x2c, 0x63, 0x0f, 0xc1, 0xc2, 0xb1, 0xe9, 0xcc, 0x38, 0x11, 0x17, 0x95, 0xfa, 0x04, 0x7d, - 0x93, 0xde, 0xe4, 0x1d, 0xfa, 0x5e, 0x95, 0xc7, 0x36, 0x21, 0x40, 0x22, 0xae, 0xec, 0xf3, 0xcd, - 0xf9, 0x66, 0xe6, 0x7c, 0xe7, 0x7c, 0x03, 0xea, 0x38, 0x62, 0x43, 0x2b, 0x08, 0xb9, 0x37, 0xf0, - 0x1c, 0x9b, 0x7b, 0x61, 0xc0, 0x6a, 0x63, 0x1a, 0xf2, 0x10, 0x15, 0xc4, 0xa7, 0x1f, 0x0d, 0xb4, - 0xdf, 0x39, 0xf8, 0xb7, 0x1d, 0xb1, 0xa1, 0x39, 0x93, 0x85, 0xc9, 0x95, 0xc7, 0x38, 0x15, 0xff, - 0xa8, 0x05, 0xc0, 0xc3, 0x11, 0x09, 0x2c, 0x3e, 0x19, 0x13, 0x55, 0xaa, 0x48, 0xd5, 0xd2, 0xc9, - 0xab, 0x5a, 0xc6, 0xaf, 0x3d, 0xc5, 0xad, 0x75, 0x63, 0x62, 0x77, 0x32, 0x26, 0x58, 0xe1, 0xd9, - 0x2f, 0xda, 0x81, 0x35, 0x11, 0xa8, 0x72, 0x45, 0xaa, 0x2a, 0x38, 0x09, 0xd0, 0x73, 0xd8, 0xf2, - 0x02, 0xc6, 0x6d, 0xdf, 0x17, 0x54, 0xcb, 0x73, 0xd5, 0x9c, 0x58, 0x2f, 0xcd, 0xc2, 0x86, 0x8b, - 0x8e, 0xa1, 0x68, 0x3b, 0x0e, 0x61, 0xcc, 0x4a, 0x76, 0xc9, 0x8b, 0xac, 0xcd, 0x04, 0x13, 0x07, - 0x22, 0x15, 0x36, 0x48, 0x60, 0xf7, 0x7d, 0xe2, 0xaa, 0x6b, 0x15, 0xa9, 0x5a, 0xc0, 0x59, 0x18, - 0xaf, 0xdc, 0x10, 0xca, 0xbc, 0x30, 0x50, 0xd7, 0x2b, 0x52, 0x35, 0x8f, 0xb3, 0x10, 0xbd, 0x80, - 0x6d, 0xdb, 0xf7, 0xc3, 0x5b, 0xe2, 0x5a, 0x11, 0x23, 0xd4, 0xf2, 0x3d, 0xc6, 0xd5, 0x8d, 0x4a, - 0xae, 0x5a, 0xc4, 0x5b, 0xe9, 0x42, 0x8f, 0x11, 0x7a, 0xe9, 0x31, 0x1e, 0xe7, 0xf6, 0xfd, 0xd0, - 0x19, 0x11, 0xd7, 0x72, 0x86, 0x36, 0x4f, 0x72, 0x0b, 0x49, 0x6e, 0xba, 0xd0, 0x18, 0xda, 0x5c, - 0xe4, 0xfe, 0x07, 0x10, 0x05, 0x54, 0x88, 0x42, 0xa8, 0xaa, 0x88, 0xeb, 0xcc, 0x20, 0xda, 0x39, - 0x28, 0x53, 0x95, 0xd0, 0x1e, 0xa0, 0x9e, 0x79, 0x61, 0xb6, 0xbe, 0x98, 0x56, 0xb7, 0x75, 0xa1, - 0x9b, 0x56, 0xf7, 0x6b, 0x5b, 0x2f, 0xff, 0x85, 0xfe, 0x06, 0xa5, 0xde, 0x4e, 0xb1, 0xb2, 0x84, - 0x10, 0x94, 0xce, 0x0d, 0xac, 0xbf, 0xaf, 0x77, 0xf4, 0x14, 0x93, 0xb5, 0x3b, 0x19, 0x9e, 0x3d, - 0xd5, 0x0b, 0x4c, 0xd8, 0x38, 0x0c, 0x18, 0x89, 0x25, 0x60, 0x91, 0x10, 0x4b, 0x34, 0xb3, 0x80, - 0xb3, 0x10, 0x99, 0xb0, 0x46, 0x28, 0x0d, 0xa9, 0x68, 0x4c, 0xe9, 0xe4, 0x74, 0xb5, 0x26, 0x67, - 0x1b, 0xd7, 0xf4, 0x98, 0x2b, 0x9a, 0x9d, 0x6c, 0x83, 0x0e, 0x01, 0x28, 0xf9, 0x16, 0x11, 0xc6, - 0xb3, 0x6e, 0x16, 0xb1, 0x92, 0x22, 0x86, 0xab, 0xfd, 0x90, 0x40, 0x99, 0x72, 0x66, 0x4b, 0xd7, - 0x31, 0x6e, 0xe1, 0xac, 0xf4, 0x5d, 0xd8, 0x6e, 0xd6, 0x2f, 0xcf, 0x5b, 0xb8, 0xa9, 0x9f, 0x59, - 0x4d, 0xbd, 0xd3, 0xa9, 0x7f, 0xd0, 0xcb, 0x12, 0xda, 0x81, 0xf2, 0x67, 0x1d, 0x77, 0x8c, 0x96, - 0x69, 0x35, 0x8d, 0x4e, 0xb3, 0xde, 0x6d, 0x7c, 0x2c, 0xcb, 0xe8, 0x00, 0xf6, 0x7a, 0x66, 0xa7, - 0xd7, 0x6e, 0xb7, 0x70, 0x57, 0x3f, 0x9b, 0xd5, 0x30, 0x17, 0x8b, 0x66, 0x98, 0x5d, 0x1d, 0x9b, - 0xf5, 0xcb, 0xe4, 0x84, 0x72, 0x5e, 0xfb, 0x29, 0xc1, 0xf1, 0x7c, 0x6d, 0x75, 0xf7, 0x86, 0x50, - 0xee, 0x31, 0x72, 0x4d, 0x02, 0x6e, 0x04, 0x83, 0x30, 0xae, 0x63, 0x1c, 0xf5, 0x7d, 0xcf, 0xb1, - 0x46, 0x64, 0x22, 0x44, 0x2b, 0x62, 0x25, 0x41, 0x2e, 0xc8, 0x64, 0x61, 0x20, 0xe5, 0xc5, 0x81, - 0x5c, 0x75, 0xb8, 0xb5, 0xef, 0xa0, 0x36, 0xc2, 0x80, 0xdb, 0x0e, 0x6f, 0x84, 0x2e, 0x79, 0x70, - 0x15, 0x64, 0xc3, 0xde, 0x82, 0x9f, 0x2d, 0x2f, 0x18, 0x84, 0xaa, 0x54, 0xc9, 0x55, 0x37, 0x4f, - 0x5e, 0x3e, 0xde, 0xaf, 0x85, 0x9a, 0xf0, 0xce, 0x78, 0x2e, 0x25, 0x46, 0xb5, 0x53, 0xd8, 0x9d, - 0xa7, 0x7e, 0x8a, 0x08, 0x9d, 0xa0, 0x23, 0xd8, 0xbc, 0x97, 0x80, 0x89, 0x03, 0x8b, 0x18, 0xa6, - 0x1a, 0x30, 0xed, 0x4e, 0x82, 0x7f, 0x96, 0x52, 0x85, 0x82, 0xf3, 0x12, 0x49, 0x2b, 0x49, 0x24, - 0x2f, 0xf5, 0xff, 0xc3, 0x6e, 0xe4, 0xe6, 0xbb, 0xb1, 0xd4, 0xc7, 0xf9, 0xa5, 0x3e, 0xd6, 0x28, - 0x1c, 0x2e, 0xbd, 0xf3, 0xd4, 0x2b, 0xef, 0x20, 0x3f, 0x23, 0xf0, 0xff, 0x8f, 0x0b, 0x3c, 0x2d, - 0x15, 0x0b, 0xc2, 0xac, 0xc9, 0xe4, 0x07, 0x26, 0xd3, 0x4c, 0x28, 0xcf, 0x93, 0x57, 0x91, 0x67, - 0x1f, 0x36, 0xc4, 0x53, 0x33, 0x95, 0x65, 0x3d, 0x0e, 0x0d, 0x57, 0xfb, 0x25, 0xc1, 0xfe, 0xa2, - 0x3d, 0x85, 0xc7, 0xd0, 0x5b, 0x28, 0xa4, 0x76, 0x63, 0x69, 0x09, 0x07, 0x4f, 0x78, 0x7a, 0x9a, - 0x1b, 0xdf, 0xfe, 0x9a, 0x30, 0x66, 0x5f, 0x11, 0x71, 0x58, 0x11, 0x67, 0x61, 0x2c, 0x7e, 0xfa, - 0x7b, 0x3f, 0xc3, 0x4a, 0x8a, 0x64, 0x6f, 0xf3, 0xc8, 0x8a, 0x37, 0xf2, 0x28, 0x71, 0xef, 0xdf, - 0xe6, 0x11, 0x4e, 0x21, 0xed, 0x35, 0x1c, 0x2d, 0x4c, 0xa7, 0x33, 0x0a, 0xc2, 0x5b, 0x9f, 0xb8, - 0x57, 0xc9, 0xa0, 0x97, 0x40, 0xf6, 0xdc, 0x54, 0x04, 0xd9, 0x73, 0xfb, 0xeb, 0xe2, 0xce, 0x6f, - 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x16, 0x49, 0x40, 0xfd, 0xcf, 0x06, 0x00, 0x00, + // 847 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x51, 0x8f, 0xdb, 0x44, + 0x10, 0xc7, 0xb1, 0x93, 0xbb, 0xc4, 0x93, 0x70, 0x97, 0xae, 0xae, 0x57, 0x53, 0x51, 0x48, 0x0d, + 0x12, 0x51, 0x91, 0x22, 0x74, 0x48, 0x50, 0xf1, 0x44, 0xb8, 0xf3, 0x1d, 0xd1, 0x5d, 0xec, 0xb0, + 0xf1, 0x51, 0x21, 0x21, 0x59, 0x8e, 0xbd, 0xd7, 0x58, 0x71, 0x6d, 0xb3, 0xbb, 0x2e, 0xca, 0x03, + 0x12, 0x9f, 0x80, 0x17, 0xde, 0xf8, 0x14, 0xa8, 0xdf, 0x81, 0xef, 0x85, 0xbc, 0xb6, 0x53, 0xc7, + 0x71, 0x73, 0x79, 0xe8, 0x93, 0x3d, 0xb3, 0x33, 0xbb, 0x3b, 0xbf, 0xd9, 0xf9, 0x83, 0x1a, 0x27, + 0x6c, 0x61, 0x87, 0x11, 0xf7, 0xef, 0x7c, 0xd7, 0xe1, 0x7e, 0x14, 0xb2, 0x61, 0x4c, 0x23, 0x1e, + 0xa1, 0xb6, 0xf8, 0xcc, 0x93, 0x3b, 0xed, 0xbf, 0x06, 0x7c, 0x3c, 0x4d, 0xd8, 0xc2, 0x28, 0x45, + 0x61, 0xf2, 0xd2, 0x67, 0x9c, 0x8a, 0x7f, 0x64, 0x02, 0xf0, 0x68, 0x49, 0x42, 0x9b, 0xaf, 0x62, + 0xa2, 0x4a, 0x7d, 0x69, 0x70, 0x74, 0xf6, 0xd5, 0xb0, 0xc8, 0x1f, 0xee, 0xca, 0x1d, 0x5a, 0x69, + 0xa2, 0xb5, 0x8a, 0x09, 0x56, 0x78, 0xf1, 0x8b, 0x4e, 0xe0, 0x40, 0x18, 0xaa, 0xdc, 0x97, 0x06, + 0x0a, 0xce, 0x0c, 0xf4, 0x05, 0x1c, 0xfb, 0x21, 0xe3, 0x4e, 0x10, 0x88, 0x54, 0xdb, 0xf7, 0xd4, + 0x86, 0x58, 0x3f, 0x2a, 0xbb, 0xc7, 0x1e, 0x7a, 0x0a, 0x5d, 0xc7, 0x75, 0x09, 0x63, 0x76, 0xb6, + 0x4b, 0x53, 0x44, 0x75, 0x32, 0x9f, 0x38, 0x10, 0xa9, 0xd0, 0x22, 0xa1, 0x33, 0x0f, 0x88, 0xa7, + 0x1e, 0xf4, 0xa5, 0x41, 0x1b, 0x17, 0x66, 0xba, 0xf2, 0x9a, 0x50, 0xe6, 0x47, 0xa1, 0x7a, 0xd8, + 0x97, 0x06, 0x4d, 0x5c, 0x98, 0xe8, 0x19, 0x3c, 0x70, 0x82, 0x20, 0xfa, 0x9d, 0x78, 0x76, 0xc2, + 0x08, 0xb5, 0x03, 0x9f, 0x71, 0xb5, 0xd5, 0x6f, 0x0c, 0xba, 0xf8, 0x38, 0x5f, 0xb8, 0x65, 0x84, + 0xde, 0xf8, 0x8c, 0xa7, 0xb1, 0xf3, 0x20, 0x72, 0x97, 0xc4, 0xb3, 0xdd, 0x85, 0xc3, 0xb3, 0xd8, + 0x76, 0x16, 0x9b, 0x2f, 0x9c, 0x2f, 0x1c, 0x2e, 0x62, 0x3f, 0x01, 0x48, 0x42, 0x2a, 0xa0, 0x10, + 0xaa, 0x2a, 0xe2, 0x3a, 0x25, 0x8f, 0x76, 0x09, 0xca, 0x9a, 0x12, 0x3a, 0x05, 0x74, 0x6b, 0x5c, + 0x1b, 0xe6, 0x0b, 0xc3, 0xb6, 0xcc, 0x6b, 0xdd, 0xb0, 0xad, 0x5f, 0xa6, 0x7a, 0xef, 0x03, 0xf4, + 0x21, 0x28, 0xa3, 0x69, 0xee, 0xeb, 0x49, 0x08, 0xc1, 0xd1, 0xe5, 0x18, 0xeb, 0x3f, 0x8c, 0x66, + 0x7a, 0xee, 0x93, 0xb5, 0x37, 0x32, 0x7c, 0xbe, 0xab, 0x17, 0x98, 0xb0, 0x38, 0x0a, 0x19, 0x49, + 0x11, 0xb0, 0x44, 0xc0, 0x12, 0xcd, 0x6c, 0xe3, 0xc2, 0x44, 0x06, 0x1c, 0x10, 0x4a, 0x23, 0x2a, + 0x1a, 0x73, 0x74, 0xf6, 0x7c, 0xbf, 0x26, 0x17, 0x1b, 0x0f, 0xf5, 0x34, 0x57, 0x34, 0x3b, 0xdb, + 0x06, 0x3d, 0x01, 0xa0, 0xe4, 0xb7, 0x84, 0x30, 0x5e, 0x74, 0xb3, 0x8b, 0x95, 0xdc, 0x33, 0xf6, + 0xb4, 0x3f, 0x25, 0x50, 0xd6, 0x39, 0xe5, 0xd2, 0x75, 0x8c, 0x4d, 0x5c, 0x94, 0xfe, 0x10, 0x1e, + 0x4c, 0x46, 0x37, 0x97, 0x26, 0x9e, 0xe8, 0x17, 0xf6, 0x44, 0x9f, 0xcd, 0x46, 0x57, 0x7a, 0x4f, + 0x42, 0x27, 0xd0, 0xfb, 0x59, 0xc7, 0xb3, 0xb1, 0x69, 0xd8, 0x93, 0xf1, 0x6c, 0x32, 0xb2, 0xce, + 0x7f, 0xec, 0xc9, 0xe8, 0x31, 0x9c, 0xde, 0x1a, 0xb3, 0xdb, 0xe9, 0xd4, 0xc4, 0x96, 0x7e, 0x51, + 0x66, 0xd8, 0x48, 0xa1, 0x8d, 0x0d, 0x4b, 0xc7, 0xc6, 0xe8, 0x26, 0x3b, 0xa1, 0xd7, 0xd4, 0xfe, + 0x92, 0xe0, 0x69, 0xb5, 0xb6, 0x91, 0xf7, 0x9a, 0x50, 0xee, 0x33, 0xf2, 0x8a, 0x84, 0x7c, 0x1c, + 0xde, 0x45, 0x69, 0x1d, 0x71, 0x32, 0x0f, 0x7c, 0xd7, 0x5e, 0x92, 0x95, 0x80, 0xd6, 0xc5, 0x4a, + 0xe6, 0xb9, 0x26, 0xab, 0xad, 0x07, 0x29, 0x6f, 0x3f, 0xc8, 0x7d, 0x1f, 0xb7, 0xf6, 0x07, 0xa8, + 0xe7, 0x51, 0xc8, 0x1d, 0x97, 0x9f, 0x47, 0x1e, 0xd9, 0xb8, 0x0a, 0x72, 0xe0, 0x74, 0x6b, 0x9e, + 0x6d, 0x3f, 0xbc, 0x8b, 0x54, 0xa9, 0xdf, 0x18, 0x74, 0xce, 0xbe, 0x7c, 0x77, 0xbf, 0xb6, 0x6a, + 0xc2, 0x27, 0x71, 0x25, 0x24, 0xf5, 0x6a, 0xcf, 0xe1, 0x61, 0x35, 0xf5, 0xa7, 0x84, 0xd0, 0x15, + 0xfa, 0x14, 0x3a, 0x6f, 0x11, 0x30, 0x71, 0x60, 0x17, 0xc3, 0x9a, 0x01, 0xd3, 0xde, 0x48, 0xf0, + 0x51, 0x6d, 0xaa, 0x20, 0x58, 0x45, 0x24, 0xed, 0x85, 0x48, 0xae, 0x9d, 0xff, 0xcd, 0x6e, 0x34, + 0xaa, 0xdd, 0xa8, 0x9d, 0xe3, 0x66, 0xed, 0x1c, 0x6b, 0x14, 0x9e, 0xd4, 0xde, 0x79, 0x3d, 0x2b, + 0xdf, 0x42, 0xb3, 0x04, 0xf8, 0xb3, 0x77, 0x03, 0x5e, 0x97, 0x8a, 0x45, 0x42, 0x79, 0xc8, 0xe4, + 0x8d, 0x21, 0xd3, 0xfe, 0x95, 0xa0, 0x57, 0xcd, 0xde, 0x87, 0xcf, 0x23, 0x68, 0x09, 0xad, 0x59, + 0x73, 0x39, 0x4c, 0xcd, 0xfb, 0x79, 0xd4, 0x70, 0x6d, 0xd6, 0x72, 0x55, 0xa1, 0xf5, 0x8a, 0x30, + 0xe6, 0xbc, 0x24, 0x42, 0x34, 0xbb, 0xb8, 0x30, 0xb5, 0xbf, 0x25, 0x78, 0xb4, 0xad, 0x00, 0x62, + 0x8c, 0xd1, 0x37, 0xd0, 0xce, 0x27, 0x9a, 0xe5, 0x94, 0x1e, 0xef, 0x90, 0x8d, 0x75, 0x6c, 0x7a, + 0xeb, 0x7c, 0xfb, 0xb7, 0x15, 0x29, 0xb9, 0xa7, 0x10, 0xf9, 0xa5, 0x9d, 0x86, 0xfb, 0x94, 0x14, + 0xd3, 0xd2, 0x71, 0xdc, 0x25, 0xce, 0x5d, 0xda, 0x3f, 0x32, 0x9c, 0x6e, 0xdf, 0x2a, 0x8e, 0x28, + 0xdf, 0x21, 0x71, 0xdf, 0x6f, 0x4a, 0xdc, 0xb3, 0x5d, 0x12, 0x97, 0x6e, 0x55, 0x2b, 0x6a, 0xef, + 0x03, 0xb7, 0xf6, 0xeb, 0x3e, 0xe2, 0x77, 0x0c, 0x9d, 0x17, 0xd8, 0x34, 0xae, 0xca, 0xca, 0x5f, + 0x11, 0x31, 0x39, 0xf5, 0x19, 0xa6, 0x65, 0x63, 0xfd, 0x6a, 0x3c, 0xb3, 0x74, 0xac, 0x5f, 0xf4, + 0x1a, 0x5a, 0x02, 0xea, 0x76, 0x41, 0xf9, 0xa3, 0xde, 0x44, 0x2f, 0x55, 0xd1, 0x7f, 0x07, 0x2d, + 0x2a, 0x6a, 0x4f, 0x9f, 0x6e, 0xda, 0xd0, 0xfe, 0x7d, 0x90, 0x70, 0x91, 0x30, 0x3f, 0x14, 0x91, + 0x5f, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x18, 0xec, 0xa3, 0x79, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index f3b5bc9a2..e88dcfd6f 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -62,15 +62,31 @@ message PushNotificationQueryResponse { message PushNotification { string access_token = 1; string chat_id = 2; + bytes public_key = 3; + string installation_id = 4; + bytes message = 5; } message PushNotificationRequest { repeated PushNotification requests = 1; - bytes message = 2; - string message_id = 3; - string ack_required = 4; + string message_id = 2; + string ack_required = 3; } -message PushNotificationAcknowledgement { - string id = 1; +message PushNotificationReport { + bool success = 1; + ErrorType error = 2; + enum ErrorType { + UNKNOWN_ERROR_TYPE = 0; + WRONG_TOKEN = 1; + INTERNAL_ERROR = 2; + NOT_REGISTERED = 3; + } + bytes public_key = 3; + string installation_id = 4; +} + +message PushNotificationResponse { + string message_id = 1; + repeated PushNotificationReport reports = 2; } diff --git a/protocol/push_notification_server/common.go b/protocol/push_notification_server/common.go new file mode 100644 index 000000000..589a6301b --- /dev/null +++ b/protocol/push_notification_server/common.go @@ -0,0 +1,58 @@ +package push_notification_server + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "github.com/status-im/status-go/eth-node/crypto" + "golang.org/x/crypto/sha3" + "io" +) + +func hashPublicKey(pk *ecdsa.PublicKey) []byte { + return shake256(crypto.CompressPubkey(pk)) +} + +func decrypt(cyphertext []byte, key []byte) ([]byte, error) { + if len(cyphertext) < nonceLength { + return nil, ErrInvalidCiphertextLength + } + + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := cyphertext[:nonceLength] + return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil) +} + +func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(reader, nonce); err != nil { + return nil, err + } + + return gcm.Seal(nonce, nonce, plaintext, nil), nil +} + +func shake256(buf []byte) []byte { + h := make([]byte, 64) + sha3.ShakeSum256(h, buf) + return h +} diff --git a/protocol/push_notification_server/gorush.go b/protocol/push_notification_server/gorush.go new file mode 100644 index 000000000..90a515b89 --- /dev/null +++ b/protocol/push_notification_server/gorush.go @@ -0,0 +1,79 @@ +package push_notification_server + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "net/http" + + "github.com/status-im/status-go/protocol/protobuf" +) + +const defaultNotificationMessage = "You have a new message" + +type GoRushRequestData struct { + EncryptedMessage string `json:"encryptedMessage"` + ChatID string `json:"chatId"` + PublicKey string `json:"publicKey"` +} + +type GoRushRequestNotification struct { + Tokens []string `json:"tokens"` + Platform uint `json:"platform"` + Message string `json:"message"` + Data *GoRushRequestData `json:"data"` +} + +type GoRushRequest struct { + Notifications []*GoRushRequestNotification `json:"notifications"` +} + +type RequestAndRegistration struct { + Request *protobuf.PushNotification + Registration *protobuf.PushNotificationRegistration +} + +func tokenTypeToGoRushPlatform(tokenType protobuf.PushNotificationRegistration_TokenType) uint { + switch tokenType { + case protobuf.PushNotificationRegistration_APN_TOKEN: + return 1 + case protobuf.PushNotificationRegistration_FIREBASE_TOKEN: + return 2 + } + return 0 +} + +func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*RequestAndRegistration) *GoRushRequest { + goRushRequests := &GoRushRequest{} + for _, requestAndRegistration := range requestAndRegistrations { + + request := requestAndRegistration.Request + registration := requestAndRegistration.Registration + goRushRequests.Notifications = append(goRushRequests.Notifications, + &GoRushRequestNotification{ + Tokens: []string{registration.Token}, + Platform: tokenTypeToGoRushPlatform(registration.TokenType), + Message: defaultNotificationMessage, + Data: &GoRushRequestData{ + EncryptedMessage: hex.EncodeToString(request.Message), + ChatID: request.ChatId, + PublicKey: hex.EncodeToString(request.PublicKey), + }, + }) + } + return goRushRequests +} + +func sendGoRushNotification(request *GoRushRequest, url string) error { + payload, err := json.Marshal(request) + if err != nil { + return err + } + + _, err = http.Post(url+"/api/push", "application/json", bytes.NewReader(payload)) + if err != nil { + return err + } + return nil + +} diff --git a/protocol/push_notification_server/gorush_test.go b/protocol/push_notification_server/gorush_test.go new file mode 100644 index 000000000..fda1de8a5 --- /dev/null +++ b/protocol/push_notification_server/gorush_test.go @@ -0,0 +1,107 @@ +package push_notification_server + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/status-im/status-go/protocol/protobuf" +) + +func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { + message1 := []byte("message-1") + message2 := []byte("message-2") + message3 := []byte("message-3") + hexMessage1 := hex.EncodeToString(message1) + hexMessage2 := hex.EncodeToString(message2) + hexMessage3 := hex.EncodeToString(message3) + chatID := "chat-id" + publicKey1 := []byte("public-key-1") + publicKey2 := []byte("public-key-2") + installationID1 := "installation-id-1" + installationID2 := "installation-id-2" + installationID3 := "installation-id-3" + var platform1 uint = 1 + var platform2 uint = 2 + var platform3 uint = 2 + token1 := "token-1" + token2 := "token-2" + token3 := "token-3" + + requestAndRegistrations := []*RequestAndRegistration{ + { + Request: &protobuf.PushNotification{ + ChatId: chatID, + PublicKey: publicKey1, + InstallationId: installationID1, + Message: message1, + }, + Registration: &protobuf.PushNotificationRegistration{ + Token: token1, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + }, + }, + { + Request: &protobuf.PushNotification{ + ChatId: chatID, + PublicKey: publicKey1, + InstallationId: installationID2, + Message: message2, + }, + Registration: &protobuf.PushNotificationRegistration{ + Token: token2, + TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, + }, + }, + { + Request: &protobuf.PushNotification{ + ChatId: chatID, + PublicKey: publicKey2, + InstallationId: installationID3, + Message: message3, + }, + Registration: &protobuf.PushNotificationRegistration{ + Token: token3, + TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, + }, + }, + } + + expectedRequests := &GoRushRequest{ + Notifications: []*GoRushRequestNotification{ + { + Tokens: []string{token1}, + Platform: platform1, + Message: defaultNotificationMessage, + Data: &GoRushRequestData{ + EncryptedMessage: hexMessage1, + ChatID: chatID, + PublicKey: hex.EncodeToString(publicKey1), + }, + }, + { + Tokens: []string{token2}, + Platform: platform2, + Message: defaultNotificationMessage, + Data: &GoRushRequestData{ + EncryptedMessage: hexMessage2, + ChatID: chatID, + PublicKey: hex.EncodeToString(publicKey1), + }, + }, + { + Tokens: []string{token3}, + Platform: platform3, + Message: defaultNotificationMessage, + Data: &GoRushRequestData{ + EncryptedMessage: hexMessage3, + ChatID: chatID, + PublicKey: hex.EncodeToString(publicKey2), + }, + }, + }, + } + actualRequests := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) + require.Equal(t, expectedRequests, actualRequests) +} diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 2d386d1a6..d9d761744 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -1,13 +1,8 @@ package push_notification_server import ( - "errors" - "io" - - "crypto/aes" - "crypto/cipher" "crypto/ecdsa" - "golang.org/x/crypto/sha3" + "errors" "github.com/golang/protobuf/proto" "github.com/google/uuid" @@ -90,7 +85,7 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrMalformedPushNotificationRegistrationInstallationID } - previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey, registration.InstallationId) + previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(publicKey), registration.InstallationId) if err != nil { return nil, err } @@ -151,6 +146,58 @@ func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue return response } +func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { + response := &protobuf.PushNotificationResponse{} + // We don't even send a response in this case + if request == nil || len(request.MessageId) == 0 { + return nil + } + + response.MessageId = request.MessageId + + // Collect successful requests & registrations + var requestAndRegistrations []*RequestAndRegistration + + for _, pn := range request.Requests { + registration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) + report := &protobuf.PushNotificationReport{ + PublicKey: pn.PublicKey, + InstallationId: pn.InstallationId, + } + + if err != nil { + // TODO: log error + report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE + } else if registration == nil { + report.Error = protobuf.PushNotificationReport_NOT_REGISTERED + } else if registration.AccessToken != pn.AccessToken { + report.Error = protobuf.PushNotificationReport_WRONG_TOKEN + } else { + // For now we just assume that the notification will be successful + requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ + Request: pn, + Registration: registration, + }) + report.Success = true + } + + response.Reports = append(response.Reports, report) + } + + if len(requestAndRegistrations) == 0 { + return response + } + + // This can be done asynchronously + goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) + err := sendGoRushNotification(goRushRequest, p.config.GorushURL) + if err != nil { + // TODO: handle this error? + } + + return response +} + func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { response := &protobuf.PushNotificationRegistrationResponse{ RequestId: shake256(payload), @@ -175,12 +222,12 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, Version: registration.Version, InstallationId: registration.InstallationId, } - if err := p.persistence.SavePushNotificationRegistration(publicKey, emptyRegistration); err != nil { + if err := p.persistence.SavePushNotificationRegistration(hashPublicKey(publicKey), emptyRegistration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR return response } - } else if err := p.persistence.SavePushNotificationRegistration(publicKey, registration); err != nil { + } else if err := p.persistence.SavePushNotificationRegistration(hashPublicKey(publicKey), registration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR return response } @@ -189,47 +236,3 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } - -func decrypt(cyphertext []byte, key []byte) ([]byte, error) { - if len(cyphertext) < nonceLength { - return nil, ErrInvalidCiphertextLength - } - - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - nonce := cyphertext[:nonceLength] - return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil) -} - -func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err = io.ReadFull(reader, nonce); err != nil { - return nil, err - } - - return gcm.Seal(nonce, nonce, plaintext, nil), nil -} - -func shake256(buf []byte) []byte { - h := make([]byte, 64) - sha3.ShakeSum256(h, buf) - return h -} diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/push_notification_server_persistence.go index 6213b70ff..f7f73cb67 100644 --- a/protocol/push_notification_server/push_notification_server_persistence.go +++ b/protocol/push_notification_server/push_notification_server_persistence.go @@ -1,26 +1,24 @@ package push_notification_server import ( - "crypto/ecdsa" "database/sql" "strings" "github.com/golang/protobuf/proto" - "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/protocol/protobuf" ) type Persistence interface { // GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id - GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) + GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) // GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) // DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id - DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error + DeletePushNotificationRegistration(publicKey []byte, installationID string) error // SavePushNotificationRegistration saves a push notification option to the db - SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error + SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error } type SQLitePersistence struct { @@ -31,9 +29,9 @@ func NewSQLitePersistence(db *sql.DB) Persistence { return &SQLitePersistence{db: db} } -func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey *ecdsa.PublicKey, installationID string) (*protobuf.PushNotificationRegistration, error) { +func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) { var marshaledRegistration []byte - err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, hashPublicKey(publicKey), installationID).Scan(&marshaledRegistration) + err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&marshaledRegistration) if err == sql.ErrNoRows { return nil, nil @@ -90,21 +88,17 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe return registrations, nil } -func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey *ecdsa.PublicKey, registration *protobuf.PushNotificationRegistration) error { +func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error { marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, hashPublicKey(publicKey), registration.InstallationId, registration.Version, marshaledRegistration) + _, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, publicKey, registration.InstallationId, registration.Version, marshaledRegistration) return err } -func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey *ecdsa.PublicKey, installationID string) error { - _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, hashPublicKey(publicKey), installationID) +func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey []byte, installationID string) error { + _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID) return err } - -func hashPublicKey(pk *ecdsa.PublicKey) []byte { - return shake256(crypto.CompressPubkey(pk)) -} diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/push_notification_server_persistence_test.go index bc0ec7052..258a0c224 100644 --- a/protocol/push_notification_server/push_notification_server_persistence_test.go +++ b/protocol/push_notification_server/push_notification_server_persistence_test.go @@ -49,9 +49,9 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { Version: 5, } - s.Require().NoError(s.persistence.SavePushNotificationRegistration(&key.PublicKey, registration)) + s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&key.PublicKey), registration)) - retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&key.PublicKey, installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&key.PublicKey), installationID) s.Require().NoError(err) s.Require().True(proto.Equal(registration, retrievedRegistration)) diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 8f5d6e8b7..19c5999da 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -149,7 +149,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(&s.key.PublicKey, &protobuf.PushNotificationRegistration{ + s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, @@ -159,7 +159,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&s.key.PublicKey, s.installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), s.installationID)) // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ @@ -346,7 +346,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(&s.key.PublicKey, &protobuf.PushNotificationRegistration{ + s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, InstallationId: s.installationID, Version: 2})) @@ -357,7 +357,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(&s.key.PublicKey, s.installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), s.installationID)) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ @@ -421,7 +421,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().True(response.Success) // Pull from the db - retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&s.key.PublicKey, s.installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&s.key.PublicKey), s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().True(proto.Equal(retrievedRegistration, registration)) @@ -442,7 +442,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().True(response.Success) // Check is gone from the db - retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(&s.key.PublicKey, s.installationID) + retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&s.key.PublicKey), s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().Empty(retrievedRegistration.AccessToken) From 15a3b710a0525b9d7925be56437971f9e0e0143b Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 3 Jul 2020 10:26:35 +0200 Subject: [PATCH 15/46] Add parsing of messages --- .../application_metadata_message.pb.go | 100 +++++++++--------- .../application_metadata_message.proto | 13 ++- protocol/protobuf/push_notifications.pb.go | 1 + protocol/protobuf/push_notifications.proto | 1 + .../push_notification.go | 4 +- protocol/v1/status_message.go | 76 +++++++++++++ 6 files changed, 134 insertions(+), 61 deletions(-) diff --git a/protocol/protobuf/application_metadata_message.pb.go b/protocol/protobuf/application_metadata_message.pb.go index 30937f2fe..37822b86a 100644 --- a/protocol/protobuf/application_metadata_message.pb.go +++ b/protocol/protobuf/application_metadata_message.pb.go @@ -38,14 +38,13 @@ const ( ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT ApplicationMetadataMessage_Type = 12 ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT ApplicationMetadataMessage_Type = 13 ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT ApplicationMetadataMessage_Type = 14 - ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTER ApplicationMetadataMessage_Type = 15 - ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE ApplicationMetadataMessage_Type = 16 - ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT ApplicationMetadataMessage_Type = 17 + ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT ApplicationMetadataMessage_Type = 15 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION ApplicationMetadataMessage_Type = 16 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE ApplicationMetadataMessage_Type = 17 ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY ApplicationMetadataMessage_Type = 18 - ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_INFO ApplicationMetadataMessage_Type = 19 - ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE ApplicationMetadataMessage_Type = 20 - ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST ApplicationMetadataMessage_Type = 21 - ApplicationMetadataMessage_PUSH_NOTIFICATION_ACKNOWLEDGMENT ApplicationMetadataMessage_Type = 22 + ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE ApplicationMetadataMessage_Type = 19 + ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST ApplicationMetadataMessage_Type = 20 + ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE ApplicationMetadataMessage_Type = 21 ) var ApplicationMetadataMessage_Type_name = map[int32]string{ @@ -64,14 +63,13 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{ 12: "SYNC_INSTALLATION_CONTACT", 13: "SYNC_INSTALLATION_ACCOUNT", 14: "SYNC_INSTALLATION_PUBLIC_CHAT", - 15: "PUSH_NOTIFICATION_REGISTER", - 16: "PUSH_NOTIFICATION_REGISTRATION_RESPONSE", - 17: "CONTACT_CODE_ADVERTISEMENT", + 15: "CONTACT_CODE_ADVERTISEMENT", + 16: "PUSH_NOTIFICATION_REGISTRATION", + 17: "PUSH_NOTIFICATION_REGISTRATION_RESPONSE", 18: "PUSH_NOTIFICATION_QUERY", - 19: "PUSH_NOTIFICATION_QUERY_INFO", - 20: "PUSH_NOTIFICATION_QUERY_RESPONSE", - 21: "PUSH_NOTIFICATION_REQUEST", - 22: "PUSH_NOTIFICATION_ACKNOWLEDGMENT", + 19: "PUSH_NOTIFICATION_QUERY_RESPONSE", + 20: "PUSH_NOTIFICATION_REQUEST", + 21: "PUSH_NOTIFICATION_RESPONSE", } var ApplicationMetadataMessage_Type_value = map[string]int32{ @@ -90,14 +88,13 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{ "SYNC_INSTALLATION_CONTACT": 12, "SYNC_INSTALLATION_ACCOUNT": 13, "SYNC_INSTALLATION_PUBLIC_CHAT": 14, - "PUSH_NOTIFICATION_REGISTER": 15, - "PUSH_NOTIFICATION_REGISTRATION_RESPONSE": 16, - "CONTACT_CODE_ADVERTISEMENT": 17, + "CONTACT_CODE_ADVERTISEMENT": 15, + "PUSH_NOTIFICATION_REGISTRATION": 16, + "PUSH_NOTIFICATION_REGISTRATION_RESPONSE": 17, "PUSH_NOTIFICATION_QUERY": 18, - "PUSH_NOTIFICATION_QUERY_INFO": 19, - "PUSH_NOTIFICATION_QUERY_RESPONSE": 20, - "PUSH_NOTIFICATION_REQUEST": 21, - "PUSH_NOTIFICATION_ACKNOWLEDGMENT": 22, + "PUSH_NOTIFICATION_QUERY_RESPONSE": 19, + "PUSH_NOTIFICATION_REQUEST": 20, + "PUSH_NOTIFICATION_RESPONSE": 21, } func (x ApplicationMetadataMessage_Type) String() string { @@ -174,35 +171,34 @@ func init() { func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) } var fileDescriptor_ad09a6406fcf24c7 = []byte{ - // 479 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xcd, 0x52, 0x13, 0x41, - 0x10, 0xc7, 0x0d, 0x1f, 0x09, 0x34, 0x31, 0x0e, 0x0d, 0x48, 0xe4, 0x43, 0x62, 0xb4, 0x14, 0xb5, - 0x2a, 0x07, 0x3d, 0x7b, 0x18, 0x66, 0x3b, 0xc9, 0x94, 0xd9, 0xd9, 0x65, 0x66, 0x56, 0x8b, 0xd3, - 0xd4, 0x22, 0x2b, 0x95, 0x2a, 0x20, 0x5b, 0x64, 0x39, 0xe4, 0x1d, 0x7d, 0x0a, 0x9f, 0xc4, 0xda, - 0x25, 0xe1, 0xc3, 0x24, 0xc5, 0x69, 0xaa, 0xff, 0xff, 0x5f, 0xf7, 0xf4, 0xf4, 0x34, 0x34, 0xe3, - 0x34, 0xbd, 0xe8, 0xff, 0x8a, 0xb3, 0xfe, 0xe0, 0xca, 0x5d, 0x26, 0x59, 0x7c, 0x16, 0x67, 0xb1, - 0xbb, 0x4c, 0x86, 0xc3, 0xf8, 0x3c, 0x69, 0xa5, 0xd7, 0x83, 0x6c, 0x80, 0x2b, 0xc5, 0x71, 0x7a, - 0xf3, 0xbb, 0xf9, 0xb7, 0x0c, 0x3b, 0xfc, 0x3e, 0xc1, 0x1f, 0xf3, 0xfe, 0x2d, 0x8e, 0x7b, 0xb0, - 0x3a, 0xec, 0x9f, 0x5f, 0xc5, 0xd9, 0xcd, 0x75, 0x52, 0x2f, 0x35, 0x4a, 0x87, 0x55, 0x7d, 0x2f, - 0x60, 0x1d, 0x2a, 0x69, 0x3c, 0xba, 0x18, 0xc4, 0x67, 0xf5, 0x85, 0xc2, 0x9b, 0x84, 0xf8, 0x0d, - 0x96, 0xb2, 0x51, 0x9a, 0xd4, 0x17, 0x1b, 0xa5, 0xc3, 0xda, 0x97, 0x8f, 0xad, 0xc9, 0x7d, 0xad, - 0xf9, 0x77, 0xb5, 0xec, 0x28, 0x4d, 0x74, 0x91, 0xd6, 0xfc, 0xb3, 0x0c, 0x4b, 0x79, 0x88, 0x6b, - 0x50, 0x89, 0xd4, 0x77, 0x15, 0xfc, 0x54, 0xec, 0x19, 0x32, 0xa8, 0x8a, 0x2e, 0xb7, 0xce, 0x27, - 0x63, 0x78, 0x87, 0x58, 0x09, 0x11, 0x6a, 0x22, 0x50, 0x96, 0x0b, 0xeb, 0xa2, 0xd0, 0xe3, 0x96, - 0xd8, 0x02, 0xee, 0xc3, 0x2b, 0x9f, 0xfc, 0x23, 0xd2, 0xa6, 0x2b, 0xc3, 0xb1, 0x7c, 0x97, 0xb2, - 0x88, 0x5b, 0xb0, 0x1e, 0x72, 0xa9, 0x9d, 0x54, 0xc6, 0xf2, 0x5e, 0x8f, 0x5b, 0x19, 0x28, 0xb6, - 0x94, 0xcb, 0xe6, 0x44, 0x89, 0xc7, 0xf2, 0x32, 0xbe, 0x85, 0x03, 0x4d, 0xc7, 0x11, 0x19, 0xeb, - 0xb8, 0xe7, 0x69, 0x32, 0xc6, 0xb5, 0x03, 0xed, 0xac, 0xe6, 0xca, 0x70, 0x51, 0x40, 0x65, 0xfc, - 0x04, 0xef, 0xb9, 0x10, 0x14, 0x5a, 0xf7, 0x14, 0x5b, 0xc1, 0xcf, 0xf0, 0xc1, 0x23, 0xd1, 0x93, - 0x8a, 0x9e, 0x84, 0x57, 0x70, 0x1b, 0x36, 0x26, 0xd0, 0x43, 0x63, 0x15, 0x37, 0x81, 0x19, 0x52, - 0xde, 0x23, 0x15, 0xf0, 0x00, 0x76, 0xff, 0xaf, 0xfd, 0x10, 0x58, 0xcb, 0x47, 0x33, 0xf5, 0x48, - 0x37, 0x1e, 0x20, 0xab, 0xce, 0xb6, 0xb9, 0x10, 0x41, 0xa4, 0x2c, 0x7b, 0x8e, 0x6f, 0x60, 0x7f, - 0xda, 0x0e, 0xa3, 0xa3, 0x9e, 0x14, 0x2e, 0xff, 0x17, 0x56, 0xc3, 0xd7, 0xb0, 0x13, 0x46, 0xa6, - 0xeb, 0x54, 0x60, 0x65, 0x5b, 0x8a, 0x5b, 0x44, 0x53, 0x47, 0x1a, 0x4b, 0x9a, 0xbd, 0xc8, 0x5f, - 0x3f, 0xcf, 0xd7, 0x93, 0xc0, 0x84, 0x81, 0x32, 0xc4, 0x58, 0x5e, 0x6c, 0xf2, 0xb9, 0x22, 0xf0, - 0xc8, 0x71, 0xef, 0x07, 0x69, 0x2b, 0x0d, 0xf9, 0xa4, 0x2c, 0x5b, 0xc7, 0x5d, 0xd8, 0x9e, 0x2e, - 0x76, 0x1c, 0x91, 0x3e, 0x61, 0x88, 0x0d, 0xd8, 0x9b, 0x63, 0x3a, 0xa9, 0xda, 0x01, 0xdb, 0xc0, - 0x77, 0xd0, 0x98, 0x47, 0xdc, 0x35, 0xb1, 0x99, 0xcf, 0x64, 0x56, 0xc7, 0xc5, 0x74, 0xd9, 0xd6, - 0xec, 0x22, 0x5c, 0xe4, 0x1b, 0xdb, 0x23, 0xaf, 0x53, 0x74, 0xfa, 0xf2, 0xb4, 0x5c, 0xac, 0xff, - 0xd7, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x68, 0x53, 0x0e, 0x30, 0x9b, 0x03, 0x00, 0x00, + // 463 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x51, 0x4f, 0x13, 0x41, + 0x10, 0xc7, 0x2d, 0x94, 0x16, 0x86, 0x5a, 0x97, 0x01, 0x42, 0x05, 0x81, 0x5a, 0x8d, 0xa2, 0x26, + 0x7d, 0xd0, 0x67, 0x1f, 0x96, 0xbd, 0x81, 0x6e, 0xec, 0xed, 0x1d, 0xbb, 0x7b, 0x1a, 0x9e, 0x36, + 0x87, 0x9c, 0xa4, 0x09, 0xd0, 0x0b, 0x3d, 0x1e, 0xfa, 0x8d, 0xfc, 0x14, 0x7e, 0x36, 0x73, 0xd7, + 0xd6, 0x82, 0x2d, 0xf0, 0x74, 0xd9, 0xff, 0xff, 0x37, 0x33, 0x99, 0xff, 0x1c, 0xb4, 0xe2, 0x34, + 0xbd, 0xec, 0xfd, 0x8c, 0xb3, 0x5e, 0xff, 0xda, 0x5d, 0x25, 0x59, 0x7c, 0x1e, 0x67, 0xb1, 0xbb, + 0x4a, 0x06, 0x83, 0xf8, 0x22, 0x69, 0xa7, 0x37, 0xfd, 0xac, 0x8f, 0xcb, 0xc5, 0xe7, 0xec, 0xf6, + 0x57, 0xeb, 0x4f, 0x05, 0xb6, 0xf9, 0xb4, 0xc0, 0x1f, 0xf3, 0xfe, 0x08, 0xc7, 0x57, 0xb0, 0x32, + 0xe8, 0x5d, 0x5c, 0xc7, 0xd9, 0xed, 0x4d, 0xd2, 0x28, 0x35, 0x4b, 0x07, 0x35, 0x3d, 0x15, 0xb0, + 0x01, 0xd5, 0x34, 0x1e, 0x5e, 0xf6, 0xe3, 0xf3, 0xc6, 0x42, 0xe1, 0x4d, 0x9e, 0xf8, 0x15, 0xca, + 0xd9, 0x30, 0x4d, 0x1a, 0x8b, 0xcd, 0xd2, 0x41, 0xfd, 0xf3, 0x87, 0xf6, 0x64, 0x5e, 0xfb, 0xe1, + 0x59, 0x6d, 0x3b, 0x4c, 0x13, 0x5d, 0x94, 0xb5, 0x7e, 0x2f, 0x41, 0x39, 0x7f, 0xe2, 0x2a, 0x54, + 0x23, 0xf5, 0x4d, 0x05, 0x3f, 0x14, 0x7b, 0x86, 0x0c, 0x6a, 0xa2, 0xc3, 0xad, 0xf3, 0xc9, 0x18, + 0x7e, 0x4c, 0xac, 0x84, 0x08, 0x75, 0x11, 0x28, 0xcb, 0x85, 0x75, 0x51, 0xe8, 0x71, 0x4b, 0x6c, + 0x01, 0x77, 0xe1, 0xa5, 0x4f, 0xfe, 0x21, 0x69, 0xd3, 0x91, 0xe1, 0x58, 0xfe, 0x57, 0xb2, 0x88, + 0x9b, 0xb0, 0x16, 0x72, 0xa9, 0x9d, 0x54, 0xc6, 0xf2, 0x6e, 0x97, 0x5b, 0x19, 0x28, 0x56, 0xce, + 0x65, 0x73, 0xaa, 0xc4, 0x7d, 0x79, 0x09, 0xdf, 0xc0, 0xbe, 0xa6, 0x93, 0x88, 0x8c, 0x75, 0xdc, + 0xf3, 0x34, 0x19, 0xe3, 0x8e, 0x02, 0xed, 0xac, 0xe6, 0xca, 0x70, 0x51, 0x40, 0x15, 0xfc, 0x08, + 0xef, 0xb8, 0x10, 0x14, 0x5a, 0xf7, 0x14, 0x5b, 0xc5, 0x4f, 0xf0, 0xde, 0x23, 0xd1, 0x95, 0x8a, + 0x9e, 0x84, 0x97, 0x71, 0x0b, 0xd6, 0x27, 0xd0, 0x5d, 0x63, 0x05, 0x37, 0x80, 0x19, 0x52, 0xde, + 0x3d, 0x15, 0x70, 0x1f, 0x76, 0xfe, 0xef, 0x7d, 0x17, 0x58, 0xcd, 0xa3, 0x99, 0x59, 0xd2, 0x8d, + 0x03, 0x64, 0xb5, 0xf9, 0x36, 0x17, 0x22, 0x88, 0x94, 0x65, 0xcf, 0xf1, 0x35, 0xec, 0xce, 0xda, + 0x61, 0x74, 0xd8, 0x95, 0xc2, 0xe5, 0x77, 0x61, 0x75, 0xdc, 0x83, 0xed, 0xc9, 0x3d, 0x44, 0xe0, + 0x91, 0xe3, 0xde, 0x77, 0xd2, 0x56, 0x1a, 0xf2, 0x49, 0x59, 0xf6, 0x02, 0x5b, 0xb0, 0x17, 0x46, + 0xa6, 0xe3, 0x54, 0x60, 0xe5, 0x91, 0x14, 0xa3, 0x16, 0x9a, 0x8e, 0xa5, 0xb1, 0x7a, 0x14, 0x39, + 0xcb, 0x13, 0x7a, 0x9c, 0x71, 0x9a, 0x4c, 0x18, 0x28, 0x43, 0x6c, 0x0d, 0x77, 0x60, 0x6b, 0x16, + 0x3e, 0x89, 0x48, 0x9f, 0x32, 0xc4, 0xb7, 0xd0, 0x7c, 0xc0, 0x9c, 0xb6, 0x58, 0xcf, 0xb7, 0x9e, + 0x37, 0xaf, 0xc8, 0x8f, 0x6d, 0xe4, 0x2b, 0xcd, 0xb3, 0xc7, 0xe5, 0x9b, 0x67, 0x95, 0xe2, 0xd7, + 0xfe, 0xf2, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xcd, 0xf5, 0xf1, 0x77, 0x03, 0x00, 0x00, } diff --git a/protocol/protobuf/application_metadata_message.proto b/protocol/protobuf/application_metadata_message.proto index a93f5dac5..705be609e 100644 --- a/protocol/protobuf/application_metadata_message.proto +++ b/protocol/protobuf/application_metadata_message.proto @@ -27,13 +27,12 @@ message ApplicationMetadataMessage { SYNC_INSTALLATION_CONTACT = 12; SYNC_INSTALLATION_ACCOUNT = 13; SYNC_INSTALLATION_PUBLIC_CHAT = 14; - PUSH_NOTIFICATION_REGISTER = 15; - PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 16; - CONTACT_CODE_ADVERTISEMENT = 17; + CONTACT_CODE_ADVERTISEMENT = 15; + PUSH_NOTIFICATION_REGISTRATION = 16; + PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17; PUSH_NOTIFICATION_QUERY = 18; - PUSH_NOTIFICATION_QUERY_INFO = 19; - PUSH_NOTIFICATION_QUERY_RESPONSE = 20; - PUSH_NOTIFICATION_REQUEST = 21; - PUSH_NOTIFICATION_ACKNOWLEDGMENT = 22; + PUSH_NOTIFICATION_QUERY_RESPONSE = 19; + PUSH_NOTIFICATION_REQUEST = 20; + PUSH_NOTIFICATION_RESPONSE = 21; } } diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index f853da320..5b1784002 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -703,6 +703,7 @@ func (m *PushNotificationReport) GetInstallationId() string { return "" } +// TOP LEVEL type PushNotificationResponse struct { MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` Reports []*PushNotificationReport `protobuf:"bytes,2,rep,name=reports,proto3" json:"reports,omitempty"` diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index e88dcfd6f..e241cb062 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -86,6 +86,7 @@ message PushNotificationReport { string installation_id = 4; } +// TOP LEVEL message PushNotificationResponse { string message_id = 1; repeated PushNotificationReport reports = 2; diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index ceae98e03..da7abd26c 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -164,8 +164,8 @@ func (p *Client) HandlePushNotificationQueryResponse(response *protobuf.PushNoti return nil } -// HandlePushNotificationAcknowledgement should set the request as processed -func (p *Client) HandlePushNotificationAcknowledgement(ack *protobuf.PushNotificationAcknowledgement) error { +// HandlePushNotificationResponse should set the request as processed +func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationResponse) error { return nil } diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index e1653e235..68f8c0497 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -319,6 +319,82 @@ func (m *StatusMessage) HandleApplication() error { return nil } + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION: + // This message is a bit different as it's encrypted, so we pass it straight through + + m.ParsedMessage = m.DecryptedPayload + + return nil + + case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT: + var message protobuf.ContactCodeAdvertisement + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode ContactCodeAdvertisement: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST: + var message protobuf.PushNotificationRequest + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode PushNotificationRequest: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE: + var message protobuf.PushNotificationRegistrationResponse + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode PushNotificationRegistrationResponse: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY: + var message protobuf.PushNotificationQuery + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode PushNotificationQuery: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE: + var message protobuf.PushNotificationQueryResponse + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode PushNotificationQueryResponse: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE: + var message protobuf.PushNotificationResponse + err := proto.Unmarshal(m.DecryptedPayload, &message) + if err != nil { + m.ParsedMessage = nil + log.Printf("[message::DecodeMessage] could not decode PushNotificationResponse: %#x, err: %v", m.Hash, err.Error()) + } else { + m.ParsedMessage = message + + return nil + } + } return nil } From 9da64ef251b7bf44496e933125368fb232ade118 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 3 Jul 2020 12:08:47 +0200 Subject: [PATCH 16/46] add basic config --- params/config.go | 3 + protocol/messenger.go | 103 ++-------------- protocol/messenger_config.go | 112 ++++++++++++++++++ .../push_notification_server.go | 3 + protocol/v1/status_message.go | 11 ++ services/ext/service.go | 14 ++- 6 files changed, 151 insertions(+), 95 deletions(-) create mode 100644 protocol/messenger_config.go diff --git a/params/config.go b/params/config.go index 54b6ba923..3ad56d79d 100644 --- a/params/config.go +++ b/params/config.go @@ -523,6 +523,9 @@ type ShhextConfig struct { VerifyENSContractAddress string VerifyTransactionChainID int64 + + // PushNotificationServerEnabled indicates whether a push notification server should be started + PushNotificationServerEnabled bool } // Validate validates the ShhextConfig struct and returns an error if inconsistent values are found diff --git a/protocol/messenger.go b/protocol/messenger.go index a1ebe3824..3a0c373b9 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3,7 +3,6 @@ package protocol import ( "context" "crypto/ecdsa" - "database/sql" "io/ioutil" "math/rand" "os" @@ -26,6 +25,7 @@ import ( "github.com/status-im/status-go/protocol/images" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/push_notification_client" + "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/transport" wakutransp "github.com/status-im/status-go/protocol/transport/waku" @@ -59,6 +59,7 @@ type Messenger struct { processor *messageProcessor handler *MessageHandler pushNotificationClient *push_notification_client.Client + pushNotificationServer *push_notification_server.Server logger *zap.Logger verifyTransactionClient EthClient featureFlags featureFlags @@ -102,99 +103,6 @@ type dbConfig struct { dbKey string } -type config struct { - // This needs to be exposed until we move here mailserver logic - // as otherwise the client is not notified of a new filter and - // won't be pulling messages from mailservers until it reloads the chats/filters - onNegotiatedFilters func([]*transport.Filter) - // DEPRECATED: no need to expose it - onSendContactCodeHandler func(*encryption.ProtocolMessageSpec) - - // systemMessagesTranslations holds translations for system-messages - systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string - // Config for the envelopes monitor - envelopesMonitorConfig *transport.EnvelopesMonitorConfig - - messagesPersistenceEnabled bool - featureFlags featureFlags - - // A path to a database or a database instance is required. - // The database instance has a higher priority. - dbConfig dbConfig - db *sql.DB - - verifyTransactionClient EthClient - - logger *zap.Logger -} - -type Option func(*config) error - -// WithSystemMessagesTranslations is required for Group Chats which are currently disabled. -// nolint: unused -func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { - return func(c *config) error { - c.systemMessagesTranslations = t - return nil - } -} - -func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option { - return func(c *config) error { - c.onNegotiatedFilters = h - return nil - } -} - -func WithCustomLogger(logger *zap.Logger) Option { - return func(c *config) error { - c.logger = logger - return nil - } -} - -func WithMessagesPersistenceEnabled() Option { - return func(c *config) error { - c.messagesPersistenceEnabled = true - return nil - } -} - -func WithDatabaseConfig(dbPath, dbKey string) Option { - return func(c *config) error { - c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey} - return nil - } -} - -func WithVerifyTransactionClient(client EthClient) Option { - return func(c *config) error { - c.verifyTransactionClient = client - return nil - } -} - -func WithDatabase(db *sql.DB) Option { - return func(c *config) error { - c.db = db - return nil - } -} - -func WithDatasync() func(c *config) error { - return func(c *config) error { - c.featureFlags.datasync = true - return nil - } -} - -func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option { - return func(c *config) error { - c.envelopesMonitorConfig = emc - return nil - } -} - func NewMessenger( identity *ecdsa.PrivateKey, node types.Node, @@ -333,6 +241,12 @@ func NewMessenger( pushNotificationClientPersistence := push_notification_client.NewPersistence(database) pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence) + var pushNotificationServer *push_notification_server.Server + if c.pushNotificationServerConfig != nil { + pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(database) + pushNotificationServer = push_notification_server.New(c.pushNotificationServerConfig, pushNotificationServerPersistence) + } + processor, err := newMessageProcessor( identity, database, @@ -357,6 +271,7 @@ func NewMessenger( processor: processor, handler: handler, pushNotificationClient: pushNotificationClient, + pushNotificationServer: pushNotificationServer, featureFlags: c.featureFlags, systemMessagesTranslations: c.systemMessagesTranslations, allChats: make(map[string]*Chat), diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go new file mode 100644 index 000000000..07dae8a73 --- /dev/null +++ b/protocol/messenger_config.go @@ -0,0 +1,112 @@ +package protocol + +import ( + "database/sql" + "github.com/status-im/status-go/protocol/encryption" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/push_notification_server" + "github.com/status-im/status-go/protocol/transport" + "go.uber.org/zap" +) + +type config struct { + // This needs to be exposed until we move here mailserver logic + // as otherwise the client is not notified of a new filter and + // won't be pulling messages from mailservers until it reloads the chats/filters + onNegotiatedFilters func([]*transport.Filter) + // DEPRECATED: no need to expose it + onSendContactCodeHandler func(*encryption.ProtocolMessageSpec) + + // systemMessagesTranslations holds translations for system-messages + systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string + // Config for the envelopes monitor + envelopesMonitorConfig *transport.EnvelopesMonitorConfig + + messagesPersistenceEnabled bool + featureFlags featureFlags + + // A path to a database or a database instance is required. + // The database instance has a higher priority. + dbConfig dbConfig + db *sql.DB + + verifyTransactionClient EthClient + + pushNotificationServerConfig *push_notification_server.Config + + logger *zap.Logger +} + +type Option func(*config) error + +// WithSystemMessagesTranslations is required for Group Chats which are currently disabled. +// nolint: unused +func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { + return func(c *config) error { + c.systemMessagesTranslations = t + return nil + } +} + +func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option { + return func(c *config) error { + c.onNegotiatedFilters = h + return nil + } +} + +func WithCustomLogger(logger *zap.Logger) Option { + return func(c *config) error { + c.logger = logger + return nil + } +} + +func WithMessagesPersistenceEnabled() Option { + return func(c *config) error { + c.messagesPersistenceEnabled = true + return nil + } +} + +func WithDatabaseConfig(dbPath, dbKey string) Option { + return func(c *config) error { + c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey} + return nil + } +} + +func WithVerifyTransactionClient(client EthClient) Option { + return func(c *config) error { + c.verifyTransactionClient = client + return nil + } +} + +func WithDatabase(db *sql.DB) Option { + return func(c *config) error { + c.db = db + return nil + } +} + +func WithPushNotificationServerConfig(pushNotificationServerConfig *push_notification_server.Config) Option { + return func(c *config) error { + c.pushNotificationServerConfig = pushNotificationServerConfig + return nil + } +} + +func WithDatasync() func(c *config) error { + return func(c *config) error { + c.featureFlags.datasync = true + return nil + } +} + +func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option { + return func(c *config) error { + c.envelopesMonitorConfig = emc + return nil + } +} diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index d9d761744..46f5b6dab 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -9,6 +9,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/protobuf" + "go.uber.org/zap" ) const encryptedPayloadKeyLength = 16 @@ -19,6 +20,8 @@ type Config struct { Identity *ecdsa.PrivateKey // GorushUrl is the url for the gorush service GorushURL string + + Logger *zap.Logger } type Server struct { diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index 68f8c0497..734863b62 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -37,6 +37,9 @@ type StatusMessage struct { // Hash is the transport layer hash Hash []byte `json:"-"` + // Dst is the targeted public key + Dst *ecdsa.PublicKey + // TransportLayerSigPubKey contains the public key provided by the transport layer TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"` // ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer @@ -87,6 +90,14 @@ func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error { m.TransportLayerSigPubKey = publicKey m.TransportPayload = shhMessage.Payload + if shhMessage.Dst != nil { + publicKey, err := crypto.UnmarshalPubkey(shhMessage.Dst) + if err != nil { + return err + } + m.Dst = publicKey + } + return nil } diff --git a/services/ext/service.go b/services/ext/service.go index b691537d2..36045c0b9 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -33,6 +33,7 @@ import ( coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol" + "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/transport" ) @@ -144,7 +145,7 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z EnvelopeEventsHandler: EnvelopeSignalHandler{}, Logger: logger, } - options := buildMessengerOptions(s.config, db, envelopesMonitorConfig, logger) + options := buildMessengerOptions(s.config, identity, db, envelopesMonitorConfig, logger) messenger, err := protocol.NewMessenger( identity, @@ -439,6 +440,7 @@ func onNegotiatedFilters(filters []*transport.Filter) { func buildMessengerOptions( config params.ShhextConfig, + identity *ecdsa.PrivateKey, db *sql.DB, envelopesMonitorConfig *transport.EnvelopesMonitorConfig, logger *zap.Logger, @@ -454,6 +456,16 @@ func buildMessengerOptions( options = append(options, protocol.WithDatasync()) } + // For now build with default/hardcoded options. + if config.PushNotificationServerEnabled { + config := &push_notification_server.Config{ + Identity: identity, + Logger: logger, + GorushURL: "https://gorush.status.im", + } + options = append(options, protocol.WithPushNotificationServerConfig(config)) + } + if config.VerifyTransactionURL != "" { client := &verifyTransactionClient{ url: config.VerifyTransactionURL, From 3afde67022b82cb59a17f9a2b8afd2a673f04c5d Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 6 Jul 2020 10:54:22 +0200 Subject: [PATCH 17/46] Move message processor to common and allow subscribing to sent messages --- protocol/common/feature_flags.go | 8 ++ protocol/{ => common}/message_processor.go | 96 ++++++++----- .../{ => common}/message_processor_test.go | 13 +- protocol/common/raw_message.go | 21 +++ protocol/message.go | 14 -- protocol/message_handler.go | 7 +- protocol/messenger.go | 130 +++++++++++------- protocol/messenger_config.go | 5 +- protocol/persistence.go | 7 +- .../push_notification.go | 44 +++++- .../push_notification_server.go | 26 +++- .../push_notification_server_test.go | 2 +- 12 files changed, 248 insertions(+), 125 deletions(-) create mode 100644 protocol/common/feature_flags.go rename protocol/{ => common}/message_processor.go (85%) rename protocol/{ => common}/message_processor_test.go (97%) create mode 100644 protocol/common/raw_message.go diff --git a/protocol/common/feature_flags.go b/protocol/common/feature_flags.go new file mode 100644 index 000000000..9473e1da1 --- /dev/null +++ b/protocol/common/feature_flags.go @@ -0,0 +1,8 @@ +package common + +type FeatureFlags struct { + // Datasync indicates whether direct messages should be sent exclusively + // using datasync, breaking change for non-v1 clients. Public messages + // are not impacted + Datasync bool +} diff --git a/protocol/message_processor.go b/protocol/common/message_processor.go similarity index 85% rename from protocol/message_processor.go rename to protocol/common/message_processor.go index d05c38625..b3d014efe 100644 --- a/protocol/message_processor.go +++ b/protocol/common/message_processor.go @@ -1,4 +1,4 @@ -package protocol +package common import ( "context" @@ -34,30 +34,33 @@ const ( whisperPoWTime = 5 ) -type messageProcessor struct { +// SentMessage reprent a message that has been passed to the transport layer +type SentMessage struct { + PublicKey *ecdsa.PublicKey + Spec *encryption.ProtocolMessageSpec + MessageIDs [][]byte +} + +type MessageProcessor struct { identity *ecdsa.PrivateKey datasync *datasync.DataSync protocol *encryption.Protocol transport transport.Transport logger *zap.Logger - featureFlags featureFlags - // onMessageSpecSent is a callback that is to be called when - // a message spec is sent. - // The reason is a callback is that datasync dispatches things asynchronously - // through a callback, and therefore return values can't be used - onMessageSpecSent func(*ecdsa.PublicKey, *encryption.ProtocolMessageSpec, [][]byte) error + subscriptions []chan<- *SentMessage + + featureFlags FeatureFlags } -func newMessageProcessor( +func NewMessageProcessor( identity *ecdsa.PrivateKey, database *sql.DB, enc *encryption.Protocol, transport transport.Transport, logger *zap.Logger, - features featureFlags, - onMessageSpecSent func(*ecdsa.PublicKey, *encryption.ProtocolMessageSpec, [][]byte) error, -) (*messageProcessor, error) { + features FeatureFlags, +) (*MessageProcessor, error) { dataSyncTransport := datasync.NewNodeTransport() dataSyncNode, err := datasyncnode.NewPersistentNode( database, @@ -70,9 +73,9 @@ func newMessageProcessor( if err != nil { return nil, err } - ds := datasync.New(dataSyncNode, dataSyncTransport, features.datasync, logger) + ds := datasync.New(dataSyncNode, dataSyncTransport, features.Datasync, logger) - p := &messageProcessor{ + p := &MessageProcessor{ identity: identity, datasync: ds, protocol: enc, @@ -85,7 +88,7 @@ func newMessageProcessor( // With DataSync enabled, messages are added to the DataSync // but actual encrypt and send calls are postponed. // sendDataSync is responsible for encrypting and sending postponed messages. - if features.datasync { + if features.Datasync { ds.Init(p.sendDataSync) ds.Start(300 * time.Millisecond) } @@ -93,12 +96,15 @@ func newMessageProcessor( return p, nil } -func (p *messageProcessor) Stop() { +func (p *MessageProcessor) Stop() { + for _, c := range p.subscriptions { + close(c) + } p.datasync.Stop() // idempotent op } // SendPrivate takes encoded data, encrypts it and sends through the wire. -func (p *messageProcessor) SendPrivate( +func (p *MessageProcessor) SendPrivate( ctx context.Context, recipient *ecdsa.PublicKey, rawMessage *RawMessage, @@ -113,7 +119,7 @@ func (p *messageProcessor) SendPrivate( // SendGroupRaw takes encoded data, encrypts it and sends through the wire, // always return the messageID -func (p *messageProcessor) SendGroup( +func (p *MessageProcessor) SendGroup( ctx context.Context, recipients []*ecdsa.PublicKey, rawMessage *RawMessage, @@ -140,7 +146,7 @@ func (p *messageProcessor) SendGroup( } // sendPrivate sends data to the recipient identifying with a given public key. -func (p *messageProcessor) sendPrivate( +func (p *MessageProcessor) sendPrivate( ctx context.Context, recipient *ecdsa.PublicKey, rawMessage *RawMessage, @@ -154,7 +160,7 @@ func (p *messageProcessor) sendPrivate( messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) - if p.featureFlags.datasync { + if p.featureFlags.Datasync { if err := p.addToDataSync(recipient, wrappedMessage); err != nil { return nil, errors.Wrap(err, "failed to send message with datasync") } @@ -180,7 +186,7 @@ func (p *messageProcessor) sendPrivate( } // sendPairInstallation sends data to the recipients, using DH -func (p *messageProcessor) SendPairInstallation( +func (p *MessageProcessor) SendPairInstallation( ctx context.Context, recipient *ecdsa.PublicKey, rawMessage *RawMessage, @@ -212,7 +218,7 @@ func (p *messageProcessor) SendPairInstallation( // EncodeMembershipUpdate takes a group and an optional chat message and returns the protobuf representation to be sent on the wire. // All the events in a group are encoded and added to the payload -func (p *messageProcessor) EncodeMembershipUpdate( +func (p *MessageProcessor) EncodeMembershipUpdate( group *v1protocol.Group, chatMessage *protobuf.ChatMessage, ) ([]byte, error) { @@ -231,7 +237,7 @@ func (p *messageProcessor) EncodeMembershipUpdate( } // SendPublic takes encoded data, encrypts it and sends through the wire. -func (p *messageProcessor) SendPublic( +func (p *MessageProcessor) SendPublic( ctx context.Context, chatName string, rawMessage *RawMessage, @@ -262,12 +268,12 @@ func (p *messageProcessor) SendPublic( return messageID, nil } -// handleMessages expects a whisper message as input, and it will go through +// HandleMessages expects a whisper message as input, and it will go through // a series of transformations until the message is parsed into an application // layer message, or in case of Raw methods, the processing stops at the layer // before. // It returns an error only if the processing of required steps failed. -func (p *messageProcessor) handleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, error) { +func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, error) { logger := p.logger.With(zap.String("site", "handleMessages")) hlogger := logger.With(zap.ByteString("hash", shhMessage.Hash)) var statusMessage v1protocol.StatusMessage @@ -305,7 +311,7 @@ func (p *messageProcessor) handleMessages(shhMessage *types.Message, application return statusMessages, nil } -func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error { +func (p *MessageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error { logger := p.logger.With(zap.String("site", "handleEncryptionLayer")) publicKey := message.SigPubKey() @@ -322,7 +328,7 @@ func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v return nil } -func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error { +func (p *MessageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error { now := time.Now().Unix() advertise, err := p.protocol.ShouldAdvertiseBundle(publicKey, now) if err != nil { @@ -351,7 +357,7 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe return nil } -func (p *messageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) { +func (p *MessageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) { wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, p.identity) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") @@ -359,7 +365,7 @@ func (p *messageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) return wrappedMessage, nil } -func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error { +func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error { groupID := datasync.ToOneToOneGroupID(&p.identity.PublicKey, publicKey) peerID := datasyncpeer.PublicKeyToPeerID(*publicKey) exist, err := p.datasync.IsPeerInGroup(groupID, peerID) @@ -381,7 +387,7 @@ func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []b // sendDataSync sends a message scheduled by the data sync layer. // Data Sync layer calls this method "dispatch" function. -func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error { +func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error { messageIDs := make([][]byte, 0, len(payload.Messages)) for _, payload := range payload.Messages { messageIDs = append(messageIDs, v1protocol.MessageID(&p.identity.PublicKey, payload.Body)) @@ -403,8 +409,8 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu } // sendMessageSpec analyses the spec properties and selects a proper transport method. -func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) { - newMessage, err := messageSpecToWhisper(messageSpec) +func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) { + newMessage, err := MessageSpecToWhisper(messageSpec) if err != nil { return nil, nil, err } @@ -425,17 +431,31 @@ func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa return nil, nil, err } - if p.onMessageSpecSent != nil { + sentMessage := &SentMessage{ + PublicKey: publicKey, + Spec: messageSpec, + MessageIDs: messageIDs, + } - if err := p.onMessageSpecSent(publicKey, messageSpec, messageIDs); err != nil { - return nil, nil, err + // Publish on channels, drop if buffer is full + for _, c := range p.subscriptions { + select { + case c <- sentMessage: + default: + logger.Warn("subscription channel full, dropping message") } } return hash, newMessage, nil } -func messageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) { +func (p *MessageProcessor) Subscribe() <-chan *SentMessage { + c := make(chan *SentMessage, 100) + p.subscriptions = append(p.subscriptions, c) + return c +} + +func MessageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) { var newMessage *types.NewMessage payload, err := proto.Marshal(spec.Message) @@ -463,8 +483,8 @@ func calculatePoW(payload []byte) float64 { return whisperDefaultPoW } -// isPubKeyEqual checks that two public keys are equal -func isPubKeyEqual(a, b *ecdsa.PublicKey) bool { +// IsPubKeyEqual checks that two public keys are equal +func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { // the curve is always the same, just compare the points return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 } diff --git a/protocol/message_processor_test.go b/protocol/common/message_processor_test.go similarity index 97% rename from protocol/message_processor_test.go rename to protocol/common/message_processor_test.go index a2c22bafe..1abdceaa3 100644 --- a/protocol/message_processor_test.go +++ b/protocol/common/message_processor_test.go @@ -1,6 +1,7 @@ -package protocol +package common import ( + "github.com/status-im/status-go/protocol" "io/ioutil" "os" "path/filepath" @@ -35,12 +36,12 @@ type MessageProcessorSuite struct { processor *messageProcessor tmpDir string - testMessage Message + testMessage protocol.Message logger *zap.Logger } func (s *MessageProcessorSuite) SetupTest() { - s.testMessage = Message{ + s.testMessage = protocol.Message{ ChatMessage: protobuf.ChatMessage{ Text: "abc123", ChatId: "testing-adamb", @@ -81,8 +82,8 @@ func (s *MessageProcessorSuite) SetupTest() { whisperConfig.MinimumAcceptedPOW = 0 shh := whisper.New(&whisperConfig) s.Require().NoError(shh.Start(nil)) - config := &config{} - s.Require().NoError(WithDatasync()(config)) + config := &protocol.config{} + s.Require().NoError(protocol.WithDatasync()(config)) whisperTransport, err := transport.NewTransport( gethbridge.NewGethWhisperWrapper(shh), @@ -100,7 +101,7 @@ func (s *MessageProcessorSuite) SetupTest() { encryptionProtocol, whisperTransport, s.logger, - featureFlags{}, + protocol.featureFlags{}, nil, ) s.Require().NoError(err) diff --git a/protocol/common/raw_message.go b/protocol/common/raw_message.go new file mode 100644 index 000000000..d21259a48 --- /dev/null +++ b/protocol/common/raw_message.go @@ -0,0 +1,21 @@ +package common + +import ( + "crypto/ecdsa" + + "github.com/status-im/status-go/protocol/protobuf" +) + +// RawMessage represent a sent or received message, kept for being able +// to re-send/propagate +type RawMessage struct { + ID string + LocalChatID string + LastSent uint64 + SendCount int + Sent bool + ResendAutomatically bool + MessageType protobuf.ApplicationMetadataMessage_Type + Payload []byte + Recipients []*ecdsa.PublicKey +} diff --git a/protocol/message.go b/protocol/message.go index 2649033eb..27c6e885b 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -109,20 +109,6 @@ type Message struct { SigPubKey *ecdsa.PublicKey `json:"-"` } -// RawMessage represent a sent or received message, kept for being able -// to re-send/propagate -type RawMessage struct { - ID string - LocalChatID string - LastSent uint64 - SendCount int - Sent bool - ResendAutomatically bool - MessageType protobuf.ApplicationMetadataMessage_Type - Payload []byte - Recipients []*ecdsa.PublicKey -} - func (m *Message) MarshalJSON() ([]byte, error) { type StickerAlias struct { Hash string `json:"hash"` diff --git a/protocol/message_handler.go b/protocol/message_handler.go index 161b3f557..b66365403 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "encoding/hex" "fmt" + "github.com/status-im/status-go/protocol/common" "github.com/pkg/errors" "go.uber.org/zap" @@ -146,7 +147,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa message.LocalChatID = chat.ID // Increase unviewed count - if !isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) { chat.UnviewedMessagesCount++ message.OutgoingStatus = "" } else { @@ -332,7 +333,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error { receivedMessage.LocalChatID = chat.ID // Increase unviewed count - if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) { chat.UnviewedMessagesCount++ } else { // Our own message, mark as sent @@ -582,7 +583,7 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, return nil, errors.New("received a public message from non-existing chat") } return chat, nil - case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey): + case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey): // It's a private message coming from us so we rely on Message.ChatID // If chat does not exist, it should be created to support multidevice synchronization. chatID := message.ChatId diff --git a/protocol/messenger.go b/protocol/messenger.go index 3a0c373b9..1076ecc3d 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3,6 +3,7 @@ package protocol import ( "context" "crypto/ecdsa" + "github.com/status-im/status-go/protocol/common" "io/ioutil" "math/rand" "os" @@ -56,13 +57,13 @@ type Messenger struct { persistence *sqlitePersistence transport transport.Transport encryptor *encryption.Protocol - processor *messageProcessor + processor *common.MessageProcessor handler *MessageHandler pushNotificationClient *push_notification_client.Client pushNotificationServer *push_notification_server.Server logger *zap.Logger verifyTransactionClient EthClient - featureFlags featureFlags + featureFlags common.FeatureFlags messagesPersistenceEnabled bool shutdownTasks []func() error systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string @@ -91,13 +92,6 @@ func (m *MessengerResponse) IsEmpty() bool { return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0 } -type featureFlags struct { - // datasync indicates whether direct messages should be sent exclusively - // using datasync, breaking change for non-v1 clients. Public messages - // are not impacted - datasync bool -} - type dbConfig struct { dbPath string dbKey string @@ -155,7 +149,7 @@ func NewMessenger( slogger := logger.With(zap.String("site", "onSendContactCodeHandler")) slogger.Debug("received a SendContactCode request") - newMessage, err := messageSpecToWhisper(messageSpec) + newMessage, err := common.MessageSpecToWhisper(messageSpec) if err != nil { slogger.Warn("failed to convert spec to Whisper message", zap.Error(err)) return @@ -238,28 +232,27 @@ func NewMessenger( logger, ) - pushNotificationClientPersistence := push_notification_client.NewPersistence(database) - pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence) - - var pushNotificationServer *push_notification_server.Server - if c.pushNotificationServerConfig != nil { - pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(database) - pushNotificationServer = push_notification_server.New(c.pushNotificationServerConfig, pushNotificationServerPersistence) - } - - processor, err := newMessageProcessor( + processor, err := common.NewMessageProcessor( identity, database, encryptionProtocol, transp, logger, c.featureFlags, - pushNotificationClient.HandleMessageSent, ) if err != nil { return nil, errors.Wrap(err, "failed to create messageProcessor") } + var pushNotificationServer *push_notification_server.Server + if c.pushNotificationServerConfig != nil { + pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(database) + pushNotificationServer = push_notification_server.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor) + } + + pushNotificationClientPersistence := push_notification_client.NewPersistence(database) + pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence, processor) + handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database}) messenger = &Messenger{ @@ -596,7 +589,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string, } m.allChats[chat.ID] = &chat - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -662,7 +655,7 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -725,7 +718,7 @@ func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, me if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -790,7 +783,7 @@ func (m *Messenger) ChangeGroupChatName(ctx context.Context, chatID string, name if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -856,7 +849,7 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -924,7 +917,7 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -992,7 +985,7 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bo if err != nil { return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE, @@ -1178,7 +1171,7 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) err return errors.New("chat not found") } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: message.Payload, MessageType: message.MessageType, @@ -1198,7 +1191,7 @@ func (m *Messenger) hasPairedDevices() bool { } // sendToPairedDevices will check if we have any paired devices and send to them if necessary -func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) error { +func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *common.RawMessage) error { hasPairedDevices := m.hasPairedDevices() // We send a message to any paired device if hasPairedDevices { @@ -1210,7 +1203,7 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) e return nil } -func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *RawMessage) ([]byte, error) { +func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) { var err error var id []byte @@ -1229,7 +1222,7 @@ func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *R return id, nil } -func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]byte, error) { +func (m *Messenger) dispatchMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) { var err error var id []byte logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", spec.LocalChatID)) @@ -1244,7 +1237,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by if err != nil { return nil, err } - if !isPubKeyEqual(publicKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(publicKey, &m.identity.PublicKey) { id, err = m.processor.SendPrivate(ctx, publicKey, spec) if err != nil { @@ -1279,7 +1272,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by // Filter out my key from the recipients n := 0 for _, recipient := range spec.Recipients { - if !isPubKeyEqual(recipient, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(recipient, &m.identity.PublicKey) { spec.Recipients[n] = recipient n++ } @@ -1379,7 +1372,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes return nil, errors.New("chat type not supported") } - id, err := m.dispatchMessage(ctx, &RawMessage{ + id, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, @@ -1494,7 +1487,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof return nil, err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chatID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE, @@ -1588,7 +1581,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons return nil, err } - _, err = m.dispatchPairInstallationMessage(ctx, &RawMessage{ + _, err = m.dispatchPairInstallationMessage(ctx, &common.RawMessage{ LocalChatID: chatID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION, @@ -1635,7 +1628,7 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error return err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chatID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT, @@ -1678,7 +1671,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { return err } - _, err = m.dispatchMessage(ctx, &RawMessage{ + _, err = m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chatID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT, @@ -1758,7 +1751,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte for _, messages := range chatWithMessages { for _, shhMessage := range messages { // TODO: fix this to use an exported method. - statusMessages, err := m.processor.handleMessages(shhMessage, true) + statusMessages, err := m.processor.HandleMessages(shhMessage, true) if err != nil { logger.Info("failed to decode messages", zap.Error(err)) continue @@ -1827,7 +1820,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } case protobuf.PairInstallation: - if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { logger.Warn("not coming from us, ignoring") continue } @@ -1840,7 +1833,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } case protobuf.SyncInstallationContact: - if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { logger.Warn("not coming from us, ignoring") continue } @@ -1853,7 +1846,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } case protobuf.SyncInstallationPublicChat: - if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { + if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) { logger.Warn("not coming from us, ignoring") continue } @@ -1926,6 +1919,43 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte logger.Warn("failed to handle ContactUpdate", zap.Error(err)) continue } + case protobuf.PushNotificationRegistration: + logger.Debug("Received PushNotificationRegistration") + if m.pushNotificationServer == nil { + continue + } + logger.Debug("Handling PushNotificationRegistration") + // TODO: Compare DST with Identity + if err := m.pushNotificationServer.HandlePushNotificationRegistration2(publicKey, msg.ParsedMessage.([]byte)); err != nil { + logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + case protobuf.PushNotificationQuery: + logger.Debug("Received PushNotificationQuery") + if m.pushNotificationServer == nil { + continue + } + logger.Debug("Handling PushNotificationQuery") + // TODO: Compare DST with Identity + if err := m.pushNotificationServer.HandlePushNotificationQuery2(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil { + logger.Warn("failed to handle PushNotificationQuery", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + case protobuf.PushNotificationRequest: + logger.Debug("Received PushNotificationRequest") + if m.pushNotificationServer == nil { + continue + } + logger.Debug("Handling PushNotificationRequest") + // TODO: Compare DST with Identity + if err := m.pushNotificationServer.HandlePushNotificationRequest2(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRequest)); err != nil { + logger.Warn("failed to handle PushNotificationRequest", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + default: logger.Debug("message not handled") @@ -2244,7 +2274,7 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr if err != nil { return nil, err } - id, err := m.dispatchMessage(ctx, &RawMessage{ + id, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION, @@ -2320,7 +2350,7 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr if err != nil { return nil, err } - id, err := m.dispatchMessage(ctx, &RawMessage{ + id, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION, @@ -2422,7 +2452,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess return nil, err } - newMessageID, err := m.dispatchMessage(ctx, &RawMessage{ + newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION, @@ -2505,7 +2535,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str return nil, err } - newMessageID, err := m.dispatchMessage(ctx, &RawMessage{ + newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION, @@ -2587,7 +2617,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes return nil, err } - newMessageID, err := m.dispatchMessage(ctx, &RawMessage{ + newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION, @@ -2684,7 +2714,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas return nil, err } - newMessageID, err := m.dispatchMessage(ctx, &RawMessage{ + newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION, @@ -2761,7 +2791,7 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract return nil, err } - newMessageID, err := m.dispatchMessage(ctx, &RawMessage{ + newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION, diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index 07dae8a73..253182277 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -2,6 +2,7 @@ package protocol import ( "database/sql" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/push_notification_server" @@ -23,7 +24,7 @@ type config struct { envelopesMonitorConfig *transport.EnvelopesMonitorConfig messagesPersistenceEnabled bool - featureFlags featureFlags + featureFlags common.FeatureFlags // A path to a database or a database instance is required. // The database instance has a higher priority. @@ -99,7 +100,7 @@ func WithPushNotificationServerConfig(pushNotificationServerConfig *push_notific func WithDatasync() func(c *config) error { return func(c *config) error { - c.featureFlags.datasync = true + c.featureFlags.Datasync = true return nil } } diff --git a/protocol/persistence.go b/protocol/persistence.go index 1b0f0bbe0..916fcdd94 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -7,6 +7,7 @@ import ( "encoding/gob" "github.com/pkg/errors" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/eth-node/crypto" ) @@ -377,7 +378,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) { return response, nil } -func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error { +func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error { var pubKeys [][]byte for _, pk := range message.Recipients { pubKeys = append(pubKeys, crypto.CompressPubkey(pk)) @@ -417,10 +418,10 @@ func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error { return err } -func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) { +func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error) { var rawPubKeys [][]byte var encodedRecipients []byte - message := &RawMessage{} + message := &common.RawMessage{} err := db.db.QueryRow(` SELECT diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index da7abd26c..50ccf1b3d 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "crypto/ecdsa" "crypto/rand" + "errors" "io" "golang.org/x/crypto/sha3" @@ -12,7 +13,7 @@ import ( "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto/ecies" - "github.com/status-im/status-go/protocol/encryption" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" ) @@ -46,6 +47,7 @@ type Config struct { type Client struct { persistence *Persistence + quit chan struct{} config *Config // lastPushNotificationVersion is the latest known push notification version @@ -58,10 +60,44 @@ type Client struct { // randomReader only used for testing so we have deterministic encryption reader io.Reader + + //messageProcessor is a message processor used to send and being notified of messages + + messageProcessor *common.MessageProcessor } -func New(persistence *Persistence) *Client { - return &Client{persistence: persistence, reader: rand.Reader} +func New(persistence *Persistence, processor *common.MessageProcessor) *Client { + return &Client{ + quit: make(chan struct{}), + messageProcessor: processor, + persistence: persistence, + reader: rand.Reader} +} + +func (c *Client) Start() error { + if c.messageProcessor == nil { + return errors.New("can't start, missing message processor") + } + + go func() { + subscription := c.messageProcessor.Subscribe() + for { + select { + case m := <-subscription: + if err := c.HandleMessageSent(m); err != nil { + // TODO: log + } + case <-c.quit: + return + } + } + }() + return nil +} + +func (c *Client) Stop() error { + close(c.quit) + return nil } // This likely will return a channel as it's an asynchrous operation @@ -78,7 +114,7 @@ func sendPushNotificationTo(publicKey *ecdsa.PublicKey, chatID string) error { // 1) Check we have reasonably fresh push notifications info // 2) Otherwise it should fetch them // 3) Send a push notification to the devices in question -func (p *Client) HandleMessageSent(publicKey *ecdsa.PublicKey, spec *encryption.ProtocolMessageSpec, messageIDs [][]byte) error { +func (p *Client) HandleMessageSent(sentMessage *common.SentMessage) error { return nil } diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 46f5b6dab..2bd4f4400 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "go.uber.org/zap" ) @@ -25,12 +26,13 @@ type Config struct { } type Server struct { - persistence Persistence - config *Config + persistence Persistence + config *Config + messageProcessor *common.MessageProcessor } -func New(config *Config, persistence Persistence) *Server { - return &Server{persistence: persistence, config: config} +func New(config *Config, persistence Persistence, messageProcessor *common.MessageProcessor) *Server { + return &Server{persistence: persistence, config: config, messageProcessor: messageProcessor} } func (p *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { @@ -239,3 +241,19 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } + +func (p *Server) HandlePushNotificationRegistration2(publicKey *ecdsa.PublicKey, payload []byte) error { + return nil + +} + +func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, query protobuf.PushNotificationQuery) error { + return nil + +} + +func (p *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, + request protobuf.PushNotificationRequest) error { + return nil + +} diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 19c5999da..7ecb4f831 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -56,7 +56,7 @@ func (s *ServerSuite) SetupTest() { Identity: identity, } - s.server = New(config, s.persistence) + s.server = New(config, s.persistence, nil) sharedKey, err := s.server.generateSharedKey(&s.key.PublicKey) s.Require().NoError(err) From d985af4a7efc791254ff67f9be0dcabce133079a Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 7 Jul 2020 11:00:04 +0200 Subject: [PATCH 18/46] Implement server persistence for client & basic tests --- .../common.go => common/crypto.go} | 23 ++- protocol/common/message_processor.go | 6 - protocol/messenger.go | 51 +++++- protocol/messenger_test.go | 3 +- .../1593601729_initial_schema.down.sql | 3 + .../1593601729_initial_schema.up.sql | 15 ++ .../migrations/doc.go | 9 ++ .../push_notification_client/persistence.go | 57 +++++++ .../persistence_test.go | 71 +++++++++ .../push_notification.go | 78 +++++++--- .../push_notification_persistence.go | 26 ---- .../push_notification_test.go | 4 +- protocol/push_notification_server/errors.go | 1 - .../push_notification_server.go | 61 ++++++-- ...sh_notification_server_persistence_test.go | 5 +- .../push_notification_server_test.go | 75 ++++----- protocol/push_notification_test.go | 145 ++++++++++++++++++ protocol/sqlite/migrations.go | 5 + services/ext/service.go | 3 + 19 files changed, 526 insertions(+), 115 deletions(-) rename protocol/{push_notification_server/common.go => common/crypto.go} (59%) create mode 100644 protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql create mode 100644 protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql create mode 100644 protocol/push_notification_client/migrations/doc.go create mode 100644 protocol/push_notification_client/persistence.go create mode 100644 protocol/push_notification_client/persistence_test.go delete mode 100644 protocol/push_notification_client/push_notification_persistence.go create mode 100644 protocol/push_notification_test.go diff --git a/protocol/push_notification_server/common.go b/protocol/common/crypto.go similarity index 59% rename from protocol/push_notification_server/common.go rename to protocol/common/crypto.go index 589a6301b..b5741b2e4 100644 --- a/protocol/push_notification_server/common.go +++ b/protocol/common/crypto.go @@ -1,19 +1,24 @@ -package push_notification_server +package common import ( "crypto/aes" "crypto/cipher" "crypto/ecdsa" + "errors" "github.com/status-im/status-go/eth-node/crypto" "golang.org/x/crypto/sha3" "io" ) -func hashPublicKey(pk *ecdsa.PublicKey) []byte { - return shake256(crypto.CompressPubkey(pk)) +const nonceLength = 12 + +var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") + +func HashPublicKey(pk *ecdsa.PublicKey) []byte { + return Shake256(crypto.CompressPubkey(pk)) } -func decrypt(cyphertext []byte, key []byte) ([]byte, error) { +func Decrypt(cyphertext []byte, key []byte) ([]byte, error) { if len(cyphertext) < nonceLength { return nil, ErrInvalidCiphertextLength } @@ -32,7 +37,7 @@ func decrypt(cyphertext []byte, key []byte) ([]byte, error) { return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil) } -func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { +func Encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err @@ -51,8 +56,14 @@ func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { return gcm.Seal(nonce, nonce, plaintext, nil), nil } -func shake256(buf []byte) []byte { +func Shake256(buf []byte) []byte { h := make([]byte, 64) sha3.ShakeSum256(h, buf) return h } + +// IsPubKeyEqual checks that two public keys are equal +func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { + // the curve is always the same, just compare the points + return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 +} diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index b3d014efe..59891f145 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -482,9 +482,3 @@ func calculatePoW(payload []byte) float64 { } return whisperDefaultPoW } - -// IsPubKeyEqual checks that two public keys are equal -func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { - // the curve is always the same, just compare the points - return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 -} diff --git a/protocol/messenger.go b/protocol/messenger.go index 1076ecc3d..a367e73b3 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -72,6 +72,7 @@ type Messenger struct { allInstallations map[string]*multidevice.Installation modifiedInstallations map[string]bool installationID string + mailserver []byte mutex sync.Mutex } @@ -2036,13 +2037,19 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte return messageState.Response, nil } +func (m *Messenger) SetMailserver(peer []byte) { + m.mailserver = peer +} + func (m *Messenger) RequestHistoricMessages( ctx context.Context, - peer []byte, // should be removed after mailserver logic is ported from, to uint32, cursor []byte, ) ([]byte, error) { - return m.transport.SendMessagesRequest(ctx, peer, from, to, cursor) + if m.mailserver == nil { + return nil, errors.New("no mailserver selected") + } + return m.transport.SendMessagesRequest(ctx, m.mailserver, from, to, cursor) } func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) { @@ -2965,6 +2972,46 @@ func (m *Messenger) Timesource() TimeSource { return m.getTimesource() } +// AddPushNotificationServer adds a push notification server +func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + return m.pushNotificationClient.AddPushNotificationServer(publicKey) +} + +// RegisterForPushNotification register deviceToken with any push notification server enabled +func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + + var contactIDs []*ecdsa.PublicKey + var mutedChatIDs []string + + m.mutex.Lock() + for _, contact := range m.allContacts { + if contact.IsAdded() { + pk, err := contact.PublicKey() + if err != nil { + m.logger.Warn("could not parse contact public key") + continue + } + contactIDs = append(contactIDs, pk) + } else if contact.IsBlocked() { + mutedChatIDs = append(mutedChatIDs, contact.ID) + } + } + for _, chat := range m.allChats { + if chat.Muted { + mutedChatIDs = append(mutedChatIDs, chat.ID) + } + + } + m.mutex.Unlock() + return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) +} + func generateAliasAndIdenticon(pk string) (string, string, error) { identicon, err := identicon.GenerateBase64(pk) if err != nil { diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index 441bbecd8..d0b3aa23a 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -2106,7 +2106,8 @@ func (s *MessengerSuite) TestRequestHistoricMessagesRequest() { m := s.newMessenger(shh) ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) defer cancel() - cursor, err := m.RequestHistoricMessages(ctx, nil, 10, 20, []byte{0x01}) + m.mailserver = []byte("mailserver-id") + cursor, err := m.RequestHistoricMessages(ctx, 10, 20, []byte{0x01}) s.EqualError(err, ctx.Err().Error()) s.Empty(cursor) // verify request is correct diff --git a/protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql b/protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql new file mode 100644 index 000000000..f0e2c5fa5 --- /dev/null +++ b/protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql @@ -0,0 +1,3 @@ +DROP TABLE push_notification_client_servers; +DROP TABLE push_notification_client_info; +DROP INDEX idx_push_notification_client_info_public_key; diff --git a/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql new file mode 100644 index 000000000..a1efbd827 --- /dev/null +++ b/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS push_notification_client_servers ( + public_key BLOB NOT NULL, + registered BOOLEAN DEFAULT FALSE, + registered_at INT NOT NULL DEFAULT 0, + UNIQUE(public_key) ON CONFLICT REPLACE +); + +CREATE TABLE IF NOT EXISTS push_notification_client_info ( + public_key BLOB NOT NULL, + installation_id TEXT NOT NULL, + access_token TEXT NOT NULL, + UNIQUE(public_key, installation_id) ON CONFLICT REPLACE +); + +CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id); diff --git a/protocol/push_notification_client/migrations/doc.go b/protocol/push_notification_client/migrations/doc.go new file mode 100644 index 000000000..0315ccce1 --- /dev/null +++ b/protocol/push_notification_client/migrations/doc.go @@ -0,0 +1,9 @@ +// This file is necessary because "github.com/status-im/migrate/v4" +// can't handle files starting with a prefix. At least that's the case +// for go-bindata. +// If go-bindata is called from the same directory, asset names +// have no prefix and "github.com/status-im/migrate/v4" works as expected. + +package migrations + +//go:generate go-bindata -pkg migrations -o ./migrations.go . diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go new file mode 100644 index 000000000..7038b2cc8 --- /dev/null +++ b/protocol/push_notification_client/persistence.go @@ -0,0 +1,57 @@ +package push_notification_client + +import ( + "crypto/ecdsa" + "database/sql" + + "github.com/status-im/status-go/eth-node/crypto" +) + +type Persistence struct { + db *sql.DB +} + +func NewPersistence(db *sql.DB) *Persistence { + return &Persistence{db: db} +} + +func (p *Persistence) TrackPushNotification(messageID []byte) error { + return nil +} + +func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { + return false, nil +} + +func (p *Persistence) SentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { + return nil +} + +func (p *Persistence) UpsertServer(server *PushNotificationServer) error { + _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at) VALUES (?,?,?)`, crypto.CompressPubkey(server.publicKey), server.registered, server.registeredAt) + return err + +} + +func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { + rows, err := p.db.Query(`SELECT public_key, registered, registered_at FROM push_notification_client_servers`) + if err != nil { + return nil, err + } + var servers []*PushNotificationServer + for rows.Next() { + server := &PushNotificationServer{} + var key []byte + err := rows.Scan(&key, &server.registered, &server.registeredAt) + if err != nil { + return nil, err + } + parsedKey, err := crypto.DecompressPubkey(key) + if err != nil { + return nil, err + } + server.publicKey = parsedKey + servers = append(servers, server) + } + return servers, nil +} diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go new file mode 100644 index 000000000..5a6c7a7ee --- /dev/null +++ b/protocol/push_notification_client/persistence_test.go @@ -0,0 +1,71 @@ +package push_notification_client + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/sqlite" +) + +func TestSQLitePersistenceSuite(t *testing.T) { + suite.Run(t, new(SQLitePersistenceSuite)) +} + +type SQLitePersistenceSuite struct { + suite.Suite + tmpFile *os.File + persistence *Persistence +} + +func (s *SQLitePersistenceSuite) SetupTest() { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + s.tmpFile = tmpFile + + database, err := sqlite.Open(s.tmpFile.Name(), "") + s.Require().NoError(err) + s.persistence = NewPersistence(database) +} + +func (s *SQLitePersistenceSuite) TearDownTest() { + _ = os.Remove(s.tmpFile.Name()) +} + +func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { + key, err := crypto.GenerateKey() + s.Require().NoError(err) + + server := &PushNotificationServer{ + publicKey: &key.PublicKey, + registered: true, + registeredAt: 1, + } + + s.Require().NoError(s.persistence.UpsertServer(server)) + + retrievedServers, err := s.persistence.GetServers() + s.Require().NoError(err) + + s.Require().Len(retrievedServers, 1) + s.Require().True(retrievedServers[0].registered) + s.Require().Equal(int64(1), retrievedServers[0].registeredAt) + s.Require().True(common.IsPubKeyEqual(retrievedServers[0].publicKey, &key.PublicKey)) + + server.registered = false + server.registeredAt = 2 + + s.Require().NoError(s.persistence.UpsertServer(server)) + + retrievedServers, err = s.persistence.GetServers() + s.Require().NoError(err) + + s.Require().Len(retrievedServers, 1) + s.Require().False(retrievedServers[0].registered) + s.Require().Equal(int64(2), retrievedServers[0].registeredAt) + s.Require().True(common.IsPubKeyEqual(retrievedServers[0].publicKey, &key.PublicKey)) +} diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 50ccf1b3d..3b94ca7f2 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -20,8 +20,15 @@ import ( const accessTokenKeyLength = 16 type PushNotificationServer struct { - key *ecdsa.PublicKey - registered bool + publicKey *ecdsa.PublicKey + registered bool + registeredAt int64 +} + +type PushNotificationInfo struct { + AccessToken string + InstallationID string + PublicKey *ecdsa.PublicKey } type Config struct { @@ -35,10 +42,6 @@ type Config struct { // AllowOnlyFromContacts indicates whether we should be receiving push notifications // only from contacts AllowOnlyFromContacts bool - // ContactIDs is the public keys for each contact that we allow notifications from - ContactIDs []*ecdsa.PublicKey - // MutedChatIDs is the IDs of the chats we don't want to receive notifications from - MutedChatIDs []string // PushNotificationServers is an array of push notification servers we want to register with PushNotificationServers []*PushNotificationServer // InstallationID is the installation-id for this device @@ -122,10 +125,10 @@ func (p *Client) NotifyOnMessageID(messageID []byte) error { return nil } -func (p *Client) mutedChatIDsHashes() [][]byte { +func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte - for _, chatID := range p.config.MutedChatIDs { + for _, chatID := range chatIDs { mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) } @@ -148,9 +151,9 @@ func (p *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, return encryptedToken, nil } -func (p *Client) allowedUserList(token []byte) ([][]byte, error) { +func (p *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([][]byte, error) { var encryptedTokens [][]byte - for _, publicKey := range p.config.ContactIDs { + for _, publicKey := range contactIDs { encryptedToken, err := p.encryptToken(publicKey, token) if err != nil { return nil, err @@ -162,9 +165,9 @@ func (p *Client) allowedUserList(token []byte) ([][]byte, error) { return encryptedTokens, nil } -func (p *Client) buildPushNotificationRegistrationMessage() (*protobuf.PushNotificationRegistration, error) { +func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { token := uuid.New().String() - allowedUserList, err := p.allowedUserList([]byte(token)) + allowedUserList, err := p.allowedUserList([]byte(token), contactIDs) if err != nil { return nil, err } @@ -175,13 +178,20 @@ func (p *Client) buildPushNotificationRegistrationMessage() (*protobuf.PushNotif InstallationId: p.config.InstallationID, Token: p.DeviceToken, Enabled: p.config.RemoteNotificationsEnabled, - BlockedChatList: p.mutedChatIDsHashes(), + BlockedChatList: p.mutedChatIDsHashes(mutedChatIDs), AllowedUserList: allowedUserList, } return options, nil } -func (p *Client) Register(deviceToken string) error { +func (p *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + servers, err := p.persistence.GetServers() + if err != nil { + return err + } + if len(servers) == 0 { + return errors.New("no servers to register with") + } return nil } @@ -205,16 +215,40 @@ func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationRe return nil } -func (p *Client) SetContactIDs(contactIDs []*ecdsa.PublicKey) error { - p.config.ContactIDs = contactIDs - // Update or schedule update - return nil +func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { + currentServers, err := c.persistence.GetServers() + if err != nil { + return err + } + + for _, server := range currentServers { + if common.IsPubKeyEqual(server.publicKey, publicKey) { + return errors.New("push notification server already added") + } + } + + return c.persistence.UpsertServer(&PushNotificationServer{ + publicKey: publicKey, + }) } -func (p *Client) SetMutedChatIDs(chatIDs []string) error { - p.config.MutedChatIDs = chatIDs - // Update or schedule update - return nil +func (c *Client) RetrievePushNotificationInfo(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) { + return nil, nil + /* + currentServers, err := c.persistence.GetServers() + if err != nil { + return err + } + + for _, server := range currentServers { + if common.IsPubKeyEqual(server.publicKey, publicKey) { + return errors.New("push notification server already added") + } + } + + return c.persistence.UpsertServer(&PushNotificationServer{ + publicKey: publicKey, + })*/ } func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { diff --git a/protocol/push_notification_client/push_notification_persistence.go b/protocol/push_notification_client/push_notification_persistence.go deleted file mode 100644 index 7305ced91..000000000 --- a/protocol/push_notification_client/push_notification_persistence.go +++ /dev/null @@ -1,26 +0,0 @@ -package push_notification_client - -import ( - "crypto/ecdsa" - "database/sql" -) - -type Persistence struct { - db *sql.DB -} - -func NewPersistence(db *sql.DB) *Persistence { - return &Persistence{db: db} -} - -func (p *Persistence) TrackPushNotification(messageID []byte) error { - return nil -} - -func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { - return false, nil -} -func (p *Persistence) SentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { - - return nil -} diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index d0b058a16..1791fd92e 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -56,8 +56,6 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { config := &Config{ Identity: identity, RemoteNotificationsEnabled: true, - MutedChatIDs: mutedChatList, - ContactIDs: contactIDs, InstallationID: myInstallationID, } @@ -77,7 +75,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { AllowedUserList: [][]byte{encryptedToken}, } - actualMessage, err := client.buildPushNotificationRegistrationMessage() + actualMessage, err := client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) require.NoError(t, err) require.Equal(t, options, actualMessage) diff --git a/protocol/push_notification_server/errors.go b/protocol/push_notification_server/errors.go index 176215483..33a78dc9c 100644 --- a/protocol/push_notification_server/errors.go +++ b/protocol/push_notification_server/errors.go @@ -7,7 +7,6 @@ var ErrEmptyPushNotificationRegistrationPayload = errors.New("empty payload") var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid installationID") var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key") var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences") -var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length") var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token") var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token") var ErrUnknownPushNotificationRegistrationTokenType = errors.New("invalid token type") diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 2bd4f4400..acc398f4d 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -1,6 +1,7 @@ package push_notification_server import ( + "context" "crypto/ecdsa" "errors" @@ -14,7 +15,6 @@ import ( ) const encryptedPayloadKeyLength = 16 -const nonceLength = 12 type Config struct { // Identity is our identity key @@ -57,7 +57,7 @@ func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) return nil, err } - return decrypt(payload, sharedKey) + return common.Decrypt(payload, sharedKey) } // ValidateRegistration validates a new message against the last one received for a given installationID and and public key @@ -90,7 +90,7 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrMalformedPushNotificationRegistrationInstallationID } - previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(publicKey), registration.InstallationId) + previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId) if err != nil { return nil, err } @@ -205,7 +205,7 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { response := &protobuf.PushNotificationRegistrationResponse{ - RequestId: shake256(payload), + RequestId: common.Shake256(payload), } registration, err := p.ValidateRegistration(publicKey, payload) @@ -227,12 +227,12 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, Version: registration.Version, InstallationId: registration.InstallationId, } - if err := p.persistence.SavePushNotificationRegistration(hashPublicKey(publicKey), emptyRegistration); err != nil { + if err := p.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR return response } - } else if err := p.persistence.SavePushNotificationRegistration(hashPublicKey(publicKey), registration); err != nil { + } else if err := p.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), registration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR return response } @@ -243,17 +243,60 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, } func (p *Server) HandlePushNotificationRegistration2(publicKey *ecdsa.PublicKey, payload []byte) error { - return nil + response := p.HandlePushNotificationRegistration(publicKey, payload) + if response == nil { + return nil + } + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE, + } + + _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err } func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, query protobuf.PushNotificationQuery) error { - return nil + response := p.HandlePushNotificationQuery(&query) + if response == nil { + return nil + } + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE, + } + + _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err } func (p *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, request protobuf.PushNotificationRequest) error { - return nil + response := p.HandlePushNotificationRequest(&request) + if response == nil { + return nil + } + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, + } + + _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err } diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/push_notification_server_persistence_test.go index 258a0c224..0f398f567 100644 --- a/protocol/push_notification_server/push_notification_server_persistence_test.go +++ b/protocol/push_notification_server/push_notification_server_persistence_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/suite" "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" "github.com/status-im/status-go/protocol/sqlite" ) @@ -49,9 +50,9 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { Version: 5, } - s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&key.PublicKey), registration)) + s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&key.PublicKey), registration)) - retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&key.PublicKey), installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&key.PublicKey), installationID) s.Require().NoError(err) s.Require().True(proto.Equal(registration, retrievedRegistration)) diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/push_notification_server_test.go index 7ecb4f831..420f09d8d 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/push_notification_server_test.go @@ -11,6 +11,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/suite" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" ) @@ -76,24 +77,24 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Invalid cyphertext length _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) - s.Require().Equal(ErrInvalidCiphertextLength, err) + s.Require().Equal(common.ErrInvalidCiphertextLength, err) // Invalid cyphertext length _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) - s.Require().Equal(ErrInvalidCiphertextLength, err) + s.Require().Equal(common.ErrInvalidCiphertextLength, err) // Invalid ciphertext _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("not too short but invalid")) - s.Require().Error(ErrInvalidCiphertextLength, err) + s.Require().Error(common.ErrInvalidCiphertextLength, err) // Different key ciphertext - cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) + cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Error(err) // Right cyphertext but non unmarshable payload - cyphertext, err = encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err) @@ -106,7 +107,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) @@ -118,7 +119,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { InstallationId: "abc", Version: 1, }) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) @@ -131,7 +132,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) @@ -145,11 +146,11 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ + s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, @@ -159,7 +160,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), s.installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID)) // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ @@ -170,7 +171,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Nil(err) @@ -183,7 +184,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) @@ -197,7 +198,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) @@ -211,7 +212,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) @@ -225,7 +226,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrUnknownPushNotificationRegistrationTokenType, err) @@ -240,7 +241,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().NoError(err) @@ -278,7 +279,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Different key ciphertext - cyphertext, err := encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) + cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -286,7 +287,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Right cyphertext but non unmarshable payload - cyphertext, err = encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -300,7 +301,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -313,7 +314,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { InstallationId: "abc", Version: 1, }) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -327,7 +328,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -342,11 +343,11 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) // Setup persistence - s.Require().NoError(s.persistence.SavePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ + s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, InstallationId: s.installationID, Version: 2})) @@ -357,7 +358,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) // Cleanup persistence - s.Require().NoError(s.persistence.DeletePushNotificationRegistration(hashPublicKey(&s.key.PublicKey), s.installationID)) + s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID)) // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ @@ -366,7 +367,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -381,7 +382,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -396,7 +397,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -414,14 +415,14 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { payload, err = proto.Marshal(registration) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) // Pull from the db - retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&s.key.PublicKey), s.installationID) + retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().True(proto.Equal(retrievedRegistration, registration)) @@ -435,25 +436,25 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { }) s.Require().NoError(err) - cyphertext, err = encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) // Check is gone from the db - retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(hashPublicKey(&s.key.PublicKey), s.installationID) + retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID) s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().Empty(retrievedRegistration.AccessToken) s.Require().Empty(retrievedRegistration.Token) s.Require().Equal(uint64(2), retrievedRegistration.Version) s.Require().Equal(s.installationID, retrievedRegistration.InstallationId) - s.Require().Equal(shake256(cyphertext), response.RequestId) + s.Require().Equal(common.Shake256(cyphertext), response.RequestId) } func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { - hashedPublicKey := hashPublicKey(&s.key.PublicKey) + hashedPublicKey := common.HashPublicKey(&s.key.PublicKey) // Successful registration := &protobuf.PushNotificationRegistration{ Token: "abc", @@ -465,7 +466,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { payload, err := proto.Marshal(registration) s.Require().NoError(err) - cyphertext, err := encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) @@ -486,7 +487,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { } func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { - hashedPublicKey := hashPublicKey(&s.key.PublicKey) + hashedPublicKey := common.HashPublicKey(&s.key.PublicKey) allowedUserList := [][]byte{[]byte("a")} // Successful @@ -501,7 +502,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { payload, err := proto.Marshal(registration) s.Require().NoError(err) - cyphertext, err := encrypt(payload, s.sharedKey, rand.Reader) + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go new file mode 100644 index 000000000..a993ef780 --- /dev/null +++ b/protocol/push_notification_test.go @@ -0,0 +1,145 @@ +package protocol + +import ( + "context" + "crypto/ecdsa" + "io/ioutil" + "os" + "testing" + + "github.com/google/uuid" + "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/tt" + "github.com/status-im/status-go/whisper/v6" +) + +func TestMessengerPushNotificationSuite(t *testing.T) { + suite.Run(t, new(MessengerPushNotificationSuite)) +} + +type MessengerPushNotificationSuite struct { + suite.Suite + m *Messenger // main instance of Messenger + privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger + // If one wants to send messages between different instances of Messenger, + // a single Whisper service should be shared. + shh types.Whisper + tmpFiles []*os.File // files to clean up + logger *zap.Logger +} + +func (s *MessengerPushNotificationSuite) SetupTest() { + s.logger = tt.MustCreateTestLogger() + + config := whisper.DefaultConfig + config.MinimumAcceptedPOW = 0 + shh := whisper.New(&config) + s.shh = gethbridge.NewGethWhisperWrapper(shh) + s.Require().NoError(shh.Start(nil)) + + s.m = s.newMessenger(s.shh) + s.privateKey = s.m.identity +} + +func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + + options := []Option{ + WithCustomLogger(s.logger), + WithMessagesPersistenceEnabled(), + WithDatabaseConfig(tmpFile.Name(), "some-key"), + WithDatasync(), + } + m, err := NewMessenger( + privateKey, + &testNode{shh: shh}, + uuid.New().String(), + options..., + ) + s.Require().NoError(err) + + err = m.Init() + s.Require().NoError(err) + + s.tmpFiles = append(s.tmpFiles, tmpFile) + + return m +} + +func (s *MessengerPushNotificationSuite) newMessenger(shh types.Whisper) *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + return s.newMessengerWithKey(s.shh, privateKey) +} + +func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { + deviceToken := "token" + + server := s.newMessenger(s.shh) + client2 := s.newMessenger(s.shh) + + err := s.m.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + err = s.m.RegisterForPushNotifications(context.Background(), deviceToken) + s.Require().NoError(err) + + info, err := client2.pushNotificationClient.RetrievePushNotificationInfo(&s.m.identity.PublicKey) + s.Require().NoError(err) + s.Require().NotNil(info) + + /* + s.Require().Len(response.Contacts, 1) + contact := response.Contacts[0] + s.Require().True(contact.IsAdded()) + + s.Require().Len(response.Chats, 1) + chat := response.Chats[0] + s.Require().False(chat.Active, "It does not create an active chat") + + // Wait for the message to reach its destination + response, err = WaitOnMessengerResponse( + s.m, + func(r *MessengerResponse) bool { return len(r.Contacts) > 0 }, + "contact request not received", + ) + s.Require().NoError(err) + + receivedContact := response.Contacts[0] + s.Require().Equal(theirName, receivedContact.Name) + s.Require().Equal(theirPicture, receivedContact.Photo) + s.Require().False(receivedContact.ENSVerified) + s.Require().True(receivedContact.HasBeenAdded()) + s.Require().NotEmpty(receivedContact.LastUpdated) + + newPicture := "new-picture" + err = theirMessenger.SendPushNotifications(context.Background(), newName, newPicture) + s.Require().NoError(err) + + // Wait for the message to reach its destination + response, err = WaitOnMessengerResponse( + s.m, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && response.Contacts[0].ID == theirContactID + }, + "contact request not received", + ) + + s.Require().NoError(err) + + receivedContact = response.Contacts[0] + s.Require().Equal(theirContactID, receivedContact.ID) + s.Require().Equal(newName, receivedContact.Name) + s.Require().Equal(newPicture, receivedContact.Photo) + s.Require().False(receivedContact.ENSVerified) + s.Require().True(receivedContact.HasBeenAdded()) + s.Require().NotEmpty(receivedContact.LastUpdated) + */ +} diff --git a/protocol/sqlite/migrations.go b/protocol/sqlite/migrations.go index f082352cd..50b381a6d 100644 --- a/protocol/sqlite/migrations.go +++ b/protocol/sqlite/migrations.go @@ -7,6 +7,7 @@ import ( encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations" appmigrations "github.com/status-im/status-go/protocol/migrations" + push_notification_client_migrations "github.com/status-im/status-go/protocol/push_notification_client/migrations" push_notification_server_migrations "github.com/status-im/status-go/protocol/push_notification_server/migrations" wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations" whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations" @@ -40,6 +41,10 @@ var defaultMigrations = []migrationsWithGetter{ Names: push_notification_server_migrations.AssetNames(), Getter: push_notification_server_migrations.Asset, }, + { + Names: push_notification_client_migrations.AssetNames(), + Getter: push_notification_client_migrations.Asset, + }, } func prepareMigrations(migrations []migrationsWithGetter) ([]string, getter, error) { diff --git a/services/ext/service.go b/services/ext/service.go index 36045c0b9..5346892fa 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -339,6 +339,9 @@ func (s *Service) DisableInstallation(installationID string) error { // UpdateMailservers updates information about selected mail servers. func (s *Service) UpdateMailservers(nodes []*enode.Node) error { + if len(nodes) > 0 && s.messenger != nil { + s.messenger.SetMailserver(nodes[0].ID().Bytes()) + } if err := s.peerStore.Update(nodes); err != nil { return err } From 7f6c8db6db6e0e2cbf027cde683e109f78ee97c7 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 7 Jul 2020 15:55:24 +0200 Subject: [PATCH 19/46] Handle push notification registration e2e --- protocol/messenger.go | 42 ++++--- .../push_notification.go | 104 +++++++++++++++--- .../push_notification_server.go | 17 ++- protocol/push_notification_test.go | 68 ++++++++++-- 4 files changed, 185 insertions(+), 46 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index a367e73b3..02c03791c 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "math/rand" "os" + "reflect" "sync" "time" @@ -252,7 +253,16 @@ func NewMessenger( } pushNotificationClientPersistence := push_notification_client.NewPersistence(database) - pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence, processor) + // Hardcoding for now + pushNotificationClientConfig := &push_notification_client.Config{ + Identity: identity, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + SendEnabled: true, + Logger: logger, + RemoteNotificationsEnabled: true, + InstallationID: installationID, + } + pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor) handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database}) @@ -1689,6 +1699,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { // RetrieveAll retrieves messages from all filters, processes them and returns a // MessengerResponse to the client func (m *Messenger) RetrieveAll() (*MessengerResponse, error) { + m.logger.Info("RETRIEVING ALL", zap.String("installation-id", m.installationID)) chatWithMessages, err := m.transport.RetrieveRawAll() if err != nil { return nil, err @@ -1920,18 +1931,6 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte logger.Warn("failed to handle ContactUpdate", zap.Error(err)) continue } - case protobuf.PushNotificationRegistration: - logger.Debug("Received PushNotificationRegistration") - if m.pushNotificationServer == nil { - continue - } - logger.Debug("Handling PushNotificationRegistration") - // TODO: Compare DST with Identity - if err := m.pushNotificationServer.HandlePushNotificationRegistration2(publicKey, msg.ParsedMessage.([]byte)); err != nil { - logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err)) - } - // We continue in any case, no changes to messenger - continue case protobuf.PushNotificationQuery: logger.Debug("Received PushNotificationQuery") if m.pushNotificationServer == nil { @@ -1958,7 +1957,22 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue default: - logger.Debug("message not handled") + // Check if is an encrypted PushNotificationRegistration + if msg.Type == protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION { + logger.Debug("Received PushNotificationRegistration") + if m.pushNotificationServer == nil { + continue + } + logger.Debug("Handling PushNotificationRegistration") + // TODO: Compare DST with Identity + if err := m.pushNotificationServer.HandlePushNotificationRegistration2(publicKey, msg.ParsedMessage.([]byte)); err != nil { + logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + } + + logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage))) } } diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 3b94ca7f2..13484d116 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -1,22 +1,25 @@ package push_notification_client import ( + "context" "crypto/aes" "crypto/cipher" "crypto/ecdsa" "crypto/rand" "errors" "io" + "time" - "golang.org/x/crypto/sha3" - + "github.com/golang/protobuf/proto" "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" + "go.uber.org/zap" ) +const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 type PushNotificationServer struct { @@ -46,6 +49,11 @@ type Config struct { PushNotificationServers []*PushNotificationServer // InstallationID is the installation-id for this device InstallationID string + + Logger *zap.Logger + + // TokenType is the type of token + TokenType protobuf.PushNotificationRegistration_TokenType } type Client struct { @@ -67,14 +75,19 @@ type Client struct { //messageProcessor is a message processor used to send and being notified of messages messageProcessor *common.MessageProcessor + //pushNotificationQueryResponses is a channel that listens to pushNotificationResponse + + pushNotificationQueryResponses chan *protobuf.PushNotificationQueryResponse } -func New(persistence *Persistence, processor *common.MessageProcessor) *Client { +func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { return &Client{ - quit: make(chan struct{}), - messageProcessor: processor, - persistence: persistence, - reader: rand.Reader} + quit: make(chan struct{}), + config: config, + pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), + messageProcessor: processor, + persistence: persistence, + reader: rand.Reader} } func (c *Client) Start() error { @@ -129,7 +142,7 @@ func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte for _, chatID := range chatIDs { - mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) + mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) } return mutedChatListHashes @@ -174,6 +187,7 @@ func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu options := &protobuf.PushNotificationRegistration{ AccessToken: token, + TokenType: p.config.TokenType, Version: p.lastPushNotificationVersion + 1, InstallationId: p.config.InstallationID, Token: p.DeviceToken, @@ -184,15 +198,53 @@ func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu return options, nil } -func (p *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { - servers, err := p.persistence.GetServers() +func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.DeviceToken = deviceToken + servers, err := c.persistence.GetServers() if err != nil { return err } + if len(servers) == 0 { return errors.New("no servers to register with") } - return nil + + registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) + if err != nil { + return err + } + + marshaledRegistration, err := proto.Marshal(registration) + if err != nil { + return err + } + + for _, server := range servers { + + encryptedRegistration, err := c.encryptRegistration(server.publicKey, marshaledRegistration) + if err != nil { + return err + } + rawMessage := &common.RawMessage{ + Payload: encryptedRegistration, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION, + } + + _, err = c.messageProcessor.SendPrivate(context.Background(), server.publicKey, rawMessage) + + // Send message and wait for reply + + } + for { + select { + case <-c.quit: + return nil + case <-time.After(5 * time.Second): + return errors.New("no query response received") + case <-c.pushNotificationQueryResponses: + return nil + } + } } // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database @@ -206,7 +258,14 @@ func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotifica } // HandlePushNotificationQueryResponse should update the data in the database for a given user -func (p *Client) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { +func (c *Client) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { + + c.config.Logger.Debug("received push notification query response", zap.Any("response", response)) + select { + case c.pushNotificationQueryResponses <- response: + default: + return errors.New("could not process push notification query response") + } return nil } @@ -216,6 +275,7 @@ func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationRe } func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { + c.config.Logger.Debug("adding push notification server", zap.Any("public-key", publicKey)) currentServers, err := c.persistence.GetServers() if err != nil { return err @@ -270,9 +330,19 @@ func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, return gcm.Seal(nonce, nonce, plaintext, nil), nil } -func shake256(input string) []byte { - buf := []byte(input) - h := make([]byte, 64) - sha3.ShakeSum256(h, buf) - return h +func (c *Client) encryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { + sharedKey, err := c.generateSharedKey(publicKey) + if err != nil { + return nil, err + } + + return common.Encrypt(payload, sharedKey, c.reader) +} + +func (c *Client) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { + return ecies.ImportECDSA(c.config.Identity).GenerateShared( + ecies.ImportECDSAPublic(publicKey), + encryptedPayloadKeyLength, + encryptedPayloadKeyLength, + ) } diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index acc398f4d..0fdb8cdfb 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -203,14 +203,14 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio return response } -func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { +func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { + + s.config.Logger.Debug("handling push notification registration") response := &protobuf.PushNotificationRegistrationResponse{ RequestId: common.Shake256(payload), } - registration, err := p.ValidateRegistration(publicKey, payload) - if registration != nil { - } + registration, err := s.ValidateRegistration(publicKey, payload) if err != nil { if err == ErrInvalidPushNotificationRegistrationVersion { @@ -218,6 +218,7 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, } else { response.Error = protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE } + s.config.Logger.Warn("registration did not validate", zap.Error(err)) return response } @@ -227,18 +228,22 @@ func (p *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, Version: registration.Version, InstallationId: registration.InstallationId, } - if err := p.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil { + if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR + s.config.Logger.Error("failed to unregister ", zap.Error(err)) return response } - } else if err := p.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), registration); err != nil { + } else if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), registration); err != nil { response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR + s.config.Logger.Error("failed to save registration", zap.Error(err)) return response } response.Success = true + s.config.Logger.Debug("handled push notification registration successfully") + return response } diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index a993ef780..bedcb87f5 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "testing" + "time" "github.com/google/uuid" "github.com/stretchr/testify/suite" @@ -14,6 +15,7 @@ import ( 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/push_notification_server" "github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/whisper/v6" ) @@ -46,16 +48,10 @@ func (s *MessengerPushNotificationSuite) SetupTest() { s.privateKey = s.m.identity } -func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Whisper, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) - options := []Option{ - WithCustomLogger(s.logger), - WithMessagesPersistenceEnabled(), - WithDatabaseConfig(tmpFile.Name(), "some-key"), - WithDatasync(), - } m, err := NewMessenger( privateKey, &testNode{shh: shh}, @@ -72,6 +68,19 @@ func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, return m } +func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + + options := []Option{ + WithCustomLogger(s.logger), + WithMessagesPersistenceEnabled(), + WithDatabaseConfig(tmpFile.Name(), "some-key"), + WithDatasync(), + } + return s.newMessengerWithOptions(shh, privateKey, options) +} + func (s *MessengerPushNotificationSuite) newMessenger(shh types.Whisper) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) @@ -79,16 +88,57 @@ func (s *MessengerPushNotificationSuite) newMessenger(shh types.Whisper) *Messen return s.newMessengerWithKey(s.shh, privateKey) } +func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Whisper) *Messenger { + privateKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + tmpFile, err := ioutil.TempFile("", "") + s.Require().NoError(err) + + serverConfig := &push_notification_server.Config{ + Logger: s.logger, + Identity: privateKey, + } + + options := []Option{ + WithCustomLogger(s.logger), + WithMessagesPersistenceEnabled(), + WithDatabaseConfig(tmpFile.Name(), "some-key"), + WithPushNotificationServerConfig(serverConfig), + WithDatasync(), + } + return s.newMessengerWithOptions(shh, privateKey, options) +} + func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { + errChan := make(chan error) + deviceToken := "token" - server := s.newMessenger(s.shh) + server := s.newPushNotificationServer(s.shh) client2 := s.newMessenger(s.shh) err := s.m.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) - err = s.m.RegisterForPushNotifications(context.Background(), deviceToken) + go func() { + err := s.m.RegisterForPushNotifications(context.Background(), deviceToken) + errChan <- err + }() + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + err = <-errChan s.Require().NoError(err) info, err := client2.pushNotificationClient.RetrievePushNotificationInfo(&s.m.identity.PublicKey) From 7e16f940de1c0a61956fc6857a11f567f0a7b8c3 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 9 Jul 2020 18:52:26 +0200 Subject: [PATCH 20/46] Handle query response --- protocol/common/message_processor.go | 4 + protocol/messenger.go | 32 +++- protocol/protobuf/push_notifications.pb.go | 119 ++++++------ protocol/protobuf/push_notifications.proto | 3 +- .../push_notification.go | 126 ++++++++---- .../push_notification_server.go | 17 +- protocol/push_notification_test.go | 179 +++++++++++++----- 7 files changed, 333 insertions(+), 147 deletions(-) diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 59891f145..eaa6246e8 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -455,6 +455,10 @@ func (p *MessageProcessor) Subscribe() <-chan *SentMessage { return c } +func (p *MessageProcessor) JoinPublic(chatID string) error { + return p.transport.JoinPublic(chatID) +} + func MessageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) { var newMessage *types.NewMessage diff --git a/protocol/messenger.go b/protocol/messenger.go index 02c03791c..e44fbf928 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1938,11 +1938,37 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } logger.Debug("Handling PushNotificationQuery") // TODO: Compare DST with Identity - if err := m.pushNotificationServer.HandlePushNotificationQuery2(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil { + if err := m.pushNotificationServer.HandlePushNotificationQuery2(publicKey, msg.ID, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil { logger.Warn("failed to handle PushNotificationQuery", zap.Error(err)) } // We continue in any case, no changes to messenger continue + case protobuf.PushNotificationRegistrationResponse: + logger.Debug("Received PushNotificationRegistrationResponse") + if m.pushNotificationClient == nil { + continue + } + logger.Debug("Handling PushNotificationRegistrationResponse") + // TODO: Compare DST with Identity + if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil { + logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + + case protobuf.PushNotificationQueryResponse: + logger.Debug("Received PushNotificationQueryResponse") + if m.pushNotificationClient == nil { + continue + } + logger.Debug("Handling PushNotificationQueryResponse") + // TODO: Compare DST with Identity + if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil { + logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue + case protobuf.PushNotificationRequest: logger.Debug("Received PushNotificationRequest") if m.pushNotificationServer == nil { @@ -2995,9 +3021,9 @@ func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ec } // RegisterForPushNotification register deviceToken with any push notification server enabled -func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { +func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]string, error) { if m.pushNotificationClient == nil { - return errors.New("push notification client not enabled") + return nil, errors.New("push notification client not enabled") } var contactIDs []*ecdsa.PublicKey diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 5b1784002..12dc55a40 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -469,7 +469,8 @@ func (m *PushNotificationQueryInfo) GetAllowedUserList() [][]byte { type PushNotificationQueryResponse struct { Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` - Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + Success bool `protobuf:"varint,3,opt,name=success,proto3" json:"success,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -507,6 +508,13 @@ func (m *PushNotificationQueryResponse) GetInfo() []*PushNotificationQueryInfo { return nil } +func (m *PushNotificationQueryResponse) GetMessageId() []byte { + if m != nil { + return m.MessageId + } + return nil +} + func (m *PushNotificationQueryResponse) GetSuccess() bool { if m != nil { return m.Success @@ -771,58 +779,59 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 847 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x51, 0x8f, 0xdb, 0x44, - 0x10, 0xc7, 0xb1, 0x93, 0xbb, 0xc4, 0x93, 0x70, 0x97, 0xae, 0xae, 0x57, 0x53, 0x51, 0x48, 0x0d, - 0x12, 0x51, 0x91, 0x22, 0x74, 0x48, 0x50, 0xf1, 0x44, 0xb8, 0xf3, 0x1d, 0xd1, 0x5d, 0xec, 0xb0, - 0xf1, 0x51, 0x21, 0x21, 0x59, 0x8e, 0xbd, 0xd7, 0x58, 0x71, 0x6d, 0xb3, 0xbb, 0x2e, 0xca, 0x03, - 0x12, 0x9f, 0x80, 0x17, 0xde, 0xf8, 0x14, 0xa8, 0xdf, 0x81, 0xef, 0x85, 0xbc, 0xb6, 0x53, 0xc7, - 0x71, 0x73, 0x79, 0xe8, 0x93, 0x3d, 0xb3, 0x33, 0xbb, 0x3b, 0xbf, 0xd9, 0xf9, 0x83, 0x1a, 0x27, - 0x6c, 0x61, 0x87, 0x11, 0xf7, 0xef, 0x7c, 0xd7, 0xe1, 0x7e, 0x14, 0xb2, 0x61, 0x4c, 0x23, 0x1e, - 0xa1, 0xb6, 0xf8, 0xcc, 0x93, 0x3b, 0xed, 0xbf, 0x06, 0x7c, 0x3c, 0x4d, 0xd8, 0xc2, 0x28, 0x45, - 0x61, 0xf2, 0xd2, 0x67, 0x9c, 0x8a, 0x7f, 0x64, 0x02, 0xf0, 0x68, 0x49, 0x42, 0x9b, 0xaf, 0x62, - 0xa2, 0x4a, 0x7d, 0x69, 0x70, 0x74, 0xf6, 0xd5, 0xb0, 0xc8, 0x1f, 0xee, 0xca, 0x1d, 0x5a, 0x69, - 0xa2, 0xb5, 0x8a, 0x09, 0x56, 0x78, 0xf1, 0x8b, 0x4e, 0xe0, 0x40, 0x18, 0xaa, 0xdc, 0x97, 0x06, - 0x0a, 0xce, 0x0c, 0xf4, 0x05, 0x1c, 0xfb, 0x21, 0xe3, 0x4e, 0x10, 0x88, 0x54, 0xdb, 0xf7, 0xd4, - 0x86, 0x58, 0x3f, 0x2a, 0xbb, 0xc7, 0x1e, 0x7a, 0x0a, 0x5d, 0xc7, 0x75, 0x09, 0x63, 0x76, 0xb6, - 0x4b, 0x53, 0x44, 0x75, 0x32, 0x9f, 0x38, 0x10, 0xa9, 0xd0, 0x22, 0xa1, 0x33, 0x0f, 0x88, 0xa7, - 0x1e, 0xf4, 0xa5, 0x41, 0x1b, 0x17, 0x66, 0xba, 0xf2, 0x9a, 0x50, 0xe6, 0x47, 0xa1, 0x7a, 0xd8, - 0x97, 0x06, 0x4d, 0x5c, 0x98, 0xe8, 0x19, 0x3c, 0x70, 0x82, 0x20, 0xfa, 0x9d, 0x78, 0x76, 0xc2, - 0x08, 0xb5, 0x03, 0x9f, 0x71, 0xb5, 0xd5, 0x6f, 0x0c, 0xba, 0xf8, 0x38, 0x5f, 0xb8, 0x65, 0x84, - 0xde, 0xf8, 0x8c, 0xa7, 0xb1, 0xf3, 0x20, 0x72, 0x97, 0xc4, 0xb3, 0xdd, 0x85, 0xc3, 0xb3, 0xd8, - 0x76, 0x16, 0x9b, 0x2f, 0x9c, 0x2f, 0x1c, 0x2e, 0x62, 0x3f, 0x01, 0x48, 0x42, 0x2a, 0xa0, 0x10, - 0xaa, 0x2a, 0xe2, 0x3a, 0x25, 0x8f, 0x76, 0x09, 0xca, 0x9a, 0x12, 0x3a, 0x05, 0x74, 0x6b, 0x5c, - 0x1b, 0xe6, 0x0b, 0xc3, 0xb6, 0xcc, 0x6b, 0xdd, 0xb0, 0xad, 0x5f, 0xa6, 0x7a, 0xef, 0x03, 0xf4, - 0x21, 0x28, 0xa3, 0x69, 0xee, 0xeb, 0x49, 0x08, 0xc1, 0xd1, 0xe5, 0x18, 0xeb, 0x3f, 0x8c, 0x66, - 0x7a, 0xee, 0x93, 0xb5, 0x37, 0x32, 0x7c, 0xbe, 0xab, 0x17, 0x98, 0xb0, 0x38, 0x0a, 0x19, 0x49, - 0x11, 0xb0, 0x44, 0xc0, 0x12, 0xcd, 0x6c, 0xe3, 0xc2, 0x44, 0x06, 0x1c, 0x10, 0x4a, 0x23, 0x2a, - 0x1a, 0x73, 0x74, 0xf6, 0x7c, 0xbf, 0x26, 0x17, 0x1b, 0x0f, 0xf5, 0x34, 0x57, 0x34, 0x3b, 0xdb, - 0x06, 0x3d, 0x01, 0xa0, 0xe4, 0xb7, 0x84, 0x30, 0x5e, 0x74, 0xb3, 0x8b, 0x95, 0xdc, 0x33, 0xf6, - 0xb4, 0x3f, 0x25, 0x50, 0xd6, 0x39, 0xe5, 0xd2, 0x75, 0x8c, 0x4d, 0x5c, 0x94, 0xfe, 0x10, 0x1e, - 0x4c, 0x46, 0x37, 0x97, 0x26, 0x9e, 0xe8, 0x17, 0xf6, 0x44, 0x9f, 0xcd, 0x46, 0x57, 0x7a, 0x4f, - 0x42, 0x27, 0xd0, 0xfb, 0x59, 0xc7, 0xb3, 0xb1, 0x69, 0xd8, 0x93, 0xf1, 0x6c, 0x32, 0xb2, 0xce, - 0x7f, 0xec, 0xc9, 0xe8, 0x31, 0x9c, 0xde, 0x1a, 0xb3, 0xdb, 0xe9, 0xd4, 0xc4, 0x96, 0x7e, 0x51, - 0x66, 0xd8, 0x48, 0xa1, 0x8d, 0x0d, 0x4b, 0xc7, 0xc6, 0xe8, 0x26, 0x3b, 0xa1, 0xd7, 0xd4, 0xfe, - 0x92, 0xe0, 0x69, 0xb5, 0xb6, 0x91, 0xf7, 0x9a, 0x50, 0xee, 0x33, 0xf2, 0x8a, 0x84, 0x7c, 0x1c, - 0xde, 0x45, 0x69, 0x1d, 0x71, 0x32, 0x0f, 0x7c, 0xd7, 0x5e, 0x92, 0x95, 0x80, 0xd6, 0xc5, 0x4a, - 0xe6, 0xb9, 0x26, 0xab, 0xad, 0x07, 0x29, 0x6f, 0x3f, 0xc8, 0x7d, 0x1f, 0xb7, 0xf6, 0x07, 0xa8, - 0xe7, 0x51, 0xc8, 0x1d, 0x97, 0x9f, 0x47, 0x1e, 0xd9, 0xb8, 0x0a, 0x72, 0xe0, 0x74, 0x6b, 0x9e, - 0x6d, 0x3f, 0xbc, 0x8b, 0x54, 0xa9, 0xdf, 0x18, 0x74, 0xce, 0xbe, 0x7c, 0x77, 0xbf, 0xb6, 0x6a, - 0xc2, 0x27, 0x71, 0x25, 0x24, 0xf5, 0x6a, 0xcf, 0xe1, 0x61, 0x35, 0xf5, 0xa7, 0x84, 0xd0, 0x15, - 0xfa, 0x14, 0x3a, 0x6f, 0x11, 0x30, 0x71, 0x60, 0x17, 0xc3, 0x9a, 0x01, 0xd3, 0xde, 0x48, 0xf0, - 0x51, 0x6d, 0xaa, 0x20, 0x58, 0x45, 0x24, 0xed, 0x85, 0x48, 0xae, 0x9d, 0xff, 0xcd, 0x6e, 0x34, - 0xaa, 0xdd, 0xa8, 0x9d, 0xe3, 0x66, 0xed, 0x1c, 0x6b, 0x14, 0x9e, 0xd4, 0xde, 0x79, 0x3d, 0x2b, - 0xdf, 0x42, 0xb3, 0x04, 0xf8, 0xb3, 0x77, 0x03, 0x5e, 0x97, 0x8a, 0x45, 0x42, 0x79, 0xc8, 0xe4, - 0x8d, 0x21, 0xd3, 0xfe, 0x95, 0xa0, 0x57, 0xcd, 0xde, 0x87, 0xcf, 0x23, 0x68, 0x09, 0xad, 0x59, - 0x73, 0x39, 0x4c, 0xcd, 0xfb, 0x79, 0xd4, 0x70, 0x6d, 0xd6, 0x72, 0x55, 0xa1, 0xf5, 0x8a, 0x30, - 0xe6, 0xbc, 0x24, 0x42, 0x34, 0xbb, 0xb8, 0x30, 0xb5, 0xbf, 0x25, 0x78, 0xb4, 0xad, 0x00, 0x62, - 0x8c, 0xd1, 0x37, 0xd0, 0xce, 0x27, 0x9a, 0xe5, 0x94, 0x1e, 0xef, 0x90, 0x8d, 0x75, 0x6c, 0x7a, - 0xeb, 0x7c, 0xfb, 0xb7, 0x15, 0x29, 0xb9, 0xa7, 0x10, 0xf9, 0xa5, 0x9d, 0x86, 0xfb, 0x94, 0x14, - 0xd3, 0xd2, 0x71, 0xdc, 0x25, 0xce, 0x5d, 0xda, 0x3f, 0x32, 0x9c, 0x6e, 0xdf, 0x2a, 0x8e, 0x28, - 0xdf, 0x21, 0x71, 0xdf, 0x6f, 0x4a, 0xdc, 0xb3, 0x5d, 0x12, 0x97, 0x6e, 0x55, 0x2b, 0x6a, 0xef, - 0x03, 0xb7, 0xf6, 0xeb, 0x3e, 0xe2, 0x77, 0x0c, 0x9d, 0x17, 0xd8, 0x34, 0xae, 0xca, 0xca, 0x5f, - 0x11, 0x31, 0x39, 0xf5, 0x19, 0xa6, 0x65, 0x63, 0xfd, 0x6a, 0x3c, 0xb3, 0x74, 0xac, 0x5f, 0xf4, - 0x1a, 0x5a, 0x02, 0xea, 0x76, 0x41, 0xf9, 0xa3, 0xde, 0x44, 0x2f, 0x55, 0xd1, 0x7f, 0x07, 0x2d, - 0x2a, 0x6a, 0x4f, 0x9f, 0x6e, 0xda, 0xd0, 0xfe, 0x7d, 0x90, 0x70, 0x91, 0x30, 0x3f, 0x14, 0x91, - 0x5f, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x18, 0xec, 0xa3, 0x79, 0x08, 0x00, 0x00, + // 857 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x51, 0x6f, 0xe3, 0x44, + 0x10, 0xc7, 0x59, 0x27, 0x6d, 0xe2, 0x49, 0x68, 0x73, 0xab, 0x5e, 0xcf, 0x9c, 0x38, 0xc8, 0x19, + 0x24, 0xa2, 0x43, 0x8a, 0x50, 0x91, 0xe0, 0xc4, 0x13, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x1b, + 0x97, 0x13, 0x12, 0x92, 0xe5, 0xc4, 0xdb, 0x8b, 0x95, 0x9c, 0x1d, 0x76, 0xd7, 0x87, 0xf2, 0x80, + 0xc4, 0x27, 0x40, 0x42, 0xbc, 0xf1, 0x29, 0xd0, 0x7d, 0x07, 0xbe, 0x17, 0xf2, 0xda, 0x0e, 0x8e, + 0xe3, 0x4b, 0xf3, 0x70, 0x4f, 0xf1, 0xfc, 0x77, 0x66, 0x77, 0xe7, 0x37, 0x3b, 0x13, 0xd0, 0x96, + 0x31, 0x9f, 0xb9, 0x61, 0x24, 0x82, 0xbb, 0x60, 0xea, 0x89, 0x20, 0x0a, 0x79, 0x7f, 0xc9, 0x22, + 0x11, 0xe1, 0xa6, 0xfc, 0x99, 0xc4, 0x77, 0xfa, 0xbf, 0x35, 0xf8, 0x70, 0x14, 0xf3, 0x99, 0x55, + 0xf0, 0x22, 0xf4, 0x65, 0xc0, 0x05, 0x93, 0xdf, 0xd8, 0x06, 0x10, 0xd1, 0x9c, 0x86, 0xae, 0x58, + 0x2d, 0xa9, 0x86, 0xba, 0xa8, 0x77, 0x74, 0xf6, 0x45, 0x3f, 0x8f, 0xef, 0xef, 0x8a, 0xed, 0x3b, + 0x49, 0xa0, 0xb3, 0x5a, 0x52, 0xa2, 0x8a, 0xfc, 0x13, 0x9f, 0xc0, 0x81, 0x34, 0x34, 0xa5, 0x8b, + 0x7a, 0x2a, 0x49, 0x0d, 0xfc, 0x19, 0x1c, 0x07, 0x21, 0x17, 0xde, 0x62, 0x21, 0x43, 0xdd, 0xc0, + 0xd7, 0x6a, 0x72, 0xfd, 0xa8, 0x28, 0x9b, 0x3e, 0x7e, 0x0a, 0x6d, 0x6f, 0x3a, 0xa5, 0x9c, 0xbb, + 0xe9, 0x2e, 0x75, 0xe9, 0xd5, 0x4a, 0x35, 0x79, 0x20, 0xd6, 0xa0, 0x41, 0x43, 0x6f, 0xb2, 0xa0, + 0xbe, 0x76, 0xd0, 0x45, 0xbd, 0x26, 0xc9, 0xcd, 0x64, 0xe5, 0x35, 0x65, 0x3c, 0x88, 0x42, 0xed, + 0xb0, 0x8b, 0x7a, 0x75, 0x92, 0x9b, 0xf8, 0x19, 0x3c, 0xf0, 0x16, 0x8b, 0xe8, 0x57, 0xea, 0xbb, + 0x31, 0xa7, 0xcc, 0x5d, 0x04, 0x5c, 0x68, 0x8d, 0x6e, 0xad, 0xd7, 0x26, 0xc7, 0xd9, 0xc2, 0x2d, + 0xa7, 0xec, 0x26, 0xe0, 0x22, 0xf1, 0x9d, 0x2c, 0xa2, 0xe9, 0x9c, 0xfa, 0xee, 0x74, 0xe6, 0x89, + 0xd4, 0xb7, 0x99, 0xfa, 0x66, 0x0b, 0xe7, 0x33, 0x4f, 0x48, 0xdf, 0x8f, 0x00, 0xe2, 0x90, 0x49, + 0x28, 0x94, 0x69, 0xaa, 0xbc, 0x4e, 0x41, 0xd1, 0x2f, 0x41, 0x5d, 0x53, 0xc2, 0xa7, 0x80, 0x6f, + 0xad, 0x6b, 0xcb, 0x7e, 0x61, 0xb9, 0x8e, 0x7d, 0x6d, 0x58, 0xae, 0xf3, 0xd3, 0xc8, 0xe8, 0xbc, + 0x87, 0xdf, 0x07, 0x75, 0x30, 0xca, 0xb4, 0x0e, 0xc2, 0x18, 0x8e, 0x2e, 0x4d, 0x62, 0x7c, 0x37, + 0x18, 0x1b, 0x99, 0xa6, 0xe8, 0x6f, 0x14, 0xf8, 0x74, 0x57, 0x2d, 0x08, 0xe5, 0xcb, 0x28, 0xe4, + 0x34, 0x41, 0xc0, 0x63, 0x09, 0x4b, 0x16, 0xb3, 0x49, 0x72, 0x13, 0x5b, 0x70, 0x40, 0x19, 0x8b, + 0x98, 0x2c, 0xcc, 0xd1, 0xd9, 0xf3, 0xfd, 0x8a, 0x9c, 0x6f, 0xdc, 0x37, 0x92, 0x58, 0x59, 0xec, + 0x74, 0x1b, 0xfc, 0x04, 0x80, 0xd1, 0x5f, 0x62, 0xca, 0x45, 0x5e, 0xcd, 0x36, 0x51, 0x33, 0xc5, + 0xf4, 0xf5, 0xdf, 0x11, 0xa8, 0xeb, 0x98, 0x62, 0xea, 0x06, 0x21, 0x36, 0xc9, 0x53, 0x7f, 0x08, + 0x0f, 0x86, 0x83, 0x9b, 0x4b, 0x9b, 0x0c, 0x8d, 0x0b, 0x77, 0x68, 0x8c, 0xc7, 0x83, 0x2b, 0xa3, + 0x83, 0xf0, 0x09, 0x74, 0x7e, 0x34, 0xc8, 0xd8, 0xb4, 0x2d, 0x77, 0x68, 0x8e, 0x87, 0x03, 0xe7, + 0xfc, 0xfb, 0x8e, 0x82, 0x1f, 0xc3, 0xe9, 0xad, 0x35, 0xbe, 0x1d, 0x8d, 0x6c, 0xe2, 0x18, 0x17, + 0x45, 0x86, 0xb5, 0x04, 0x9a, 0x69, 0x39, 0x06, 0xb1, 0x06, 0x37, 0xe9, 0x09, 0x9d, 0xba, 0xfe, + 0x07, 0x82, 0xa7, 0xe5, 0xdc, 0x06, 0xfe, 0x6b, 0xca, 0x44, 0xc0, 0xe9, 0x2b, 0x1a, 0x0a, 0x33, + 0xbc, 0x8b, 0x92, 0x3c, 0x96, 0xf1, 0x64, 0x11, 0x4c, 0xdd, 0x39, 0x5d, 0x49, 0x68, 0x6d, 0xa2, + 0xa6, 0xca, 0x35, 0x5d, 0x6d, 0x3d, 0x48, 0x65, 0xfb, 0x41, 0xee, 0xfb, 0xb8, 0xf5, 0xdf, 0x40, + 0x3b, 0x8f, 0x42, 0xe1, 0x4d, 0xc5, 0x79, 0xe4, 0xd3, 0x8d, 0xab, 0x60, 0x0f, 0x4e, 0xb7, 0xfa, + 0xd9, 0x0d, 0xc2, 0xbb, 0x48, 0x43, 0xdd, 0x5a, 0xaf, 0x75, 0xf6, 0xf9, 0xdb, 0xeb, 0xb5, 0x95, + 0x13, 0x39, 0x59, 0x96, 0x5c, 0x12, 0x55, 0x7f, 0x0e, 0x0f, 0xcb, 0xa1, 0x3f, 0xc4, 0x94, 0xad, + 0xf0, 0xc7, 0xd0, 0xfa, 0x1f, 0x01, 0x97, 0x07, 0xb6, 0x09, 0xac, 0x19, 0x70, 0xfd, 0x0d, 0x82, + 0x0f, 0x2a, 0x43, 0x25, 0xc1, 0x32, 0x22, 0xb4, 0x17, 0x22, 0xa5, 0xb2, 0xff, 0x37, 0xab, 0x51, + 0x2b, 0x57, 0xa3, 0xb2, 0x8f, 0xeb, 0x95, 0x7d, 0xac, 0xff, 0x89, 0xe0, 0x49, 0xe5, 0xa5, 0xd7, + 0xcd, 0xf2, 0x35, 0xd4, 0x0b, 0x84, 0x3f, 0x79, 0x3b, 0xe1, 0x75, 0xae, 0x44, 0x06, 0x24, 0xb7, + 0x7c, 0x45, 0x39, 0xf7, 0x5e, 0xd2, 0x3c, 0x93, 0x36, 0x51, 0x33, 0xc5, 0xf4, 0x8b, 0x4d, 0x58, + 0xdb, 0x68, 0x42, 0xfd, 0x1f, 0x04, 0x9d, 0xf2, 0xe6, 0xfb, 0xf0, 0x7b, 0x04, 0x0d, 0x39, 0x8b, + 0xd6, 0xdc, 0x0e, 0x13, 0xf3, 0x7e, 0x5e, 0x15, 0xdc, 0xeb, 0x95, 0xdc, 0x35, 0x68, 0x64, 0xf7, + 0x97, 0x43, 0xb5, 0x4d, 0x72, 0x53, 0xff, 0x0b, 0xc1, 0xa3, 0xed, 0x09, 0x21, 0xdb, 0x1c, 0x7f, + 0x05, 0xcd, 0xac, 0xe3, 0x79, 0x06, 0xf1, 0xf1, 0x8e, 0xb1, 0xb2, 0xf6, 0xad, 0xe0, 0xa7, 0x16, + 0xf9, 0x49, 0x20, 0x73, 0x37, 0x71, 0x0f, 0x18, 0xcd, 0xbb, 0xa9, 0xe5, 0x4d, 0xe7, 0x24, 0x93, + 0xf4, 0xbf, 0x15, 0x38, 0xdd, 0xbe, 0xd5, 0x32, 0x62, 0x62, 0xc7, 0x08, 0xfc, 0x76, 0x73, 0x04, + 0x3e, 0xdb, 0x35, 0x02, 0x93, 0xad, 0x2a, 0x87, 0xde, 0xbb, 0xc0, 0xad, 0xff, 0xbc, 0xcf, 0x70, + 0x3c, 0x86, 0xd6, 0x0b, 0x62, 0x5b, 0x57, 0xc5, 0x7f, 0x86, 0xd2, 0x90, 0x53, 0x12, 0xcd, 0xb2, + 0x1d, 0x97, 0x18, 0x57, 0xe6, 0xd8, 0x31, 0x88, 0x71, 0xd1, 0xa9, 0xe9, 0x31, 0x68, 0xdb, 0x09, + 0x65, 0x6f, 0x7e, 0x13, 0x3d, 0x2a, 0xa3, 0xff, 0x06, 0x1a, 0x4c, 0xe6, 0xce, 0x35, 0x45, 0x16, + 0xb4, 0x7b, 0x1f, 0x24, 0x92, 0x07, 0x4c, 0x0e, 0xa5, 0xe7, 0x97, 0xff, 0x05, 0x00, 0x00, 0xff, + 0xff, 0x8b, 0x53, 0x27, 0x32, 0x99, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index e241cb062..fbcd74dc2 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -56,7 +56,8 @@ message PushNotificationQueryInfo { message PushNotificationQueryResponse { repeated PushNotificationQueryInfo info = 1; - bool success = 2; + bytes message_id = 2; + bool success = 3; } message PushNotification { diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 13484d116..48b115e45 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -4,8 +4,11 @@ import ( "context" "crypto/aes" "crypto/cipher" + + "bytes" "crypto/ecdsa" "crypto/rand" + "encoding/hex" "errors" "io" "time" @@ -75,19 +78,21 @@ type Client struct { //messageProcessor is a message processor used to send and being notified of messages messageProcessor *common.MessageProcessor + //pushNotificationRegistrationResponses is a channel that listens to pushNotificationResponse + pushNotificationRegistrationResponses chan *protobuf.PushNotificationRegistrationResponse //pushNotificationQueryResponses is a channel that listens to pushNotificationResponse - pushNotificationQueryResponses chan *protobuf.PushNotificationQueryResponse } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { return &Client{ - quit: make(chan struct{}), - config: config, - pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), - messageProcessor: processor, - persistence: persistence, - reader: rand.Reader} + quit: make(chan struct{}), + config: config, + pushNotificationRegistrationResponses: make(chan *protobuf.PushNotificationRegistrationResponse), + pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), + messageProcessor: processor, + persistence: persistence, + reader: rand.Reader} } func (c *Client) Start() error { @@ -116,11 +121,6 @@ func (c *Client) Stop() error { return nil } -// This likely will return a channel as it's an asynchrous operation -func fetchNotificationInfoFor(publicKey *ecdsa.PublicKey) error { - return nil -} - // Sends an actual push notification, where do we get the chatID? func sendPushNotificationTo(publicKey *ecdsa.PublicKey, chatID string) error { return nil @@ -198,32 +198,32 @@ func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu return options, nil } -func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { +func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]string, error) { c.DeviceToken = deviceToken servers, err := c.persistence.GetServers() if err != nil { - return err + return nil, err } if len(servers) == 0 { - return errors.New("no servers to register with") + return nil, errors.New("no servers to register with") } registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) if err != nil { - return err + return nil, err } marshaledRegistration, err := proto.Marshal(registration) if err != nil { - return err + return nil, err } for _, server := range servers { encryptedRegistration, err := c.encryptRegistration(server.publicKey, marshaledRegistration) if err != nil { - return err + return nil, err } rawMessage := &common.RawMessage{ Payload: encryptedRegistration, @@ -235,20 +235,27 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut // Send message and wait for reply } + // TODO: this needs to wait for all the registrations, probably best to poll the database for { select { case <-c.quit: - return nil + return nil, nil case <-time.After(5 * time.Second): - return errors.New("no query response received") - case <-c.pushNotificationQueryResponses: - return nil + return nil, errors.New("no registration response received") + case <-c.pushNotificationRegistrationResponses: + return nil, nil } } } // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database -func (p *Client) HandlePushNotificationRegistrationResponse(response *protobuf.PushNotificationRegistrationResponse) error { +func (c *Client) HandlePushNotificationRegistrationResponse(response protobuf.PushNotificationRegistrationResponse) error { + c.config.Logger.Debug("received push notification registration response", zap.Any("response", response)) + select { + case c.pushNotificationRegistrationResponses <- &response: + default: + return errors.New("could not process push notification registration response") + } return nil } @@ -258,11 +265,11 @@ func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotifica } // HandlePushNotificationQueryResponse should update the data in the database for a given user -func (c *Client) HandlePushNotificationQueryResponse(response *protobuf.PushNotificationQueryResponse) error { +func (c *Client) HandlePushNotificationQueryResponse(response protobuf.PushNotificationQueryResponse) error { c.config.Logger.Debug("received push notification query response", zap.Any("response", response)) select { - case c.pushNotificationQueryResponses <- response: + case c.pushNotificationQueryResponses <- &response: default: return errors.New("could not process push notification query response") } @@ -293,22 +300,65 @@ func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { } func (c *Client) RetrievePushNotificationInfo(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) { - return nil, nil - /* - currentServers, err := c.persistence.GetServers() - if err != nil { - return err - } + hashedPublicKey := common.HashPublicKey(publicKey) + query := &protobuf.PushNotificationQuery{ + PublicKeys: [][]byte{hashedPublicKey}, + } + encodedMessage, err := proto.Marshal(query) + if err != nil { + return nil, err + } - for _, server := range currentServers { - if common.IsPubKeyEqual(server.publicKey, publicKey) { - return errors.New("push notification server already added") + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY, + } + + encodedPublicKey := hex.EncodeToString(hashedPublicKey) + c.config.Logger.Debug("sending query") + messageID, err := c.messageProcessor.SendPublic(context.Background(), encodedPublicKey, rawMessage) + + // TODO: this is probably best done by polling the database instead + for { + select { + case <-c.quit: + return nil, nil + case <-time.After(5 * time.Second): + return nil, errors.New("no registration query response received") + case response := <-c.pushNotificationQueryResponses: + if bytes.Compare(response.MessageId, messageID) != 0 { + // Not for us, queue back + c.pushNotificationQueryResponses <- response + // This is not accurate, we should then shrink the timeout + // Also we should handle multiple responses + continue } - } - return c.persistence.UpsertServer(&PushNotificationServer{ - publicKey: publicKey, - })*/ + if len(response.Info) == 0 { + return nil, errors.New("empty response from the server") + } + + var pushNotificationInfo []*PushNotificationInfo + for _, info := range response.Info { + if bytes.Compare(info.PublicKey, hashedPublicKey) != 0 { + continue + } + pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ + PublicKey: publicKey, + AccessToken: info.AccessToken, + InstallationID: info.InstallationId, + }) + + } + + return pushNotificationInfo, nil + } + } +} + +func (s *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { + encodedPublicKey := hex.EncodeToString(hashedPublicKey) + return s.messageProcessor.JoinPublic(encodedPublicKey) } func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/push_notification_server.go index 0fdb8cdfb..be238fb13 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/push_notification_server.go @@ -3,6 +3,7 @@ package push_notification_server import ( "context" "crypto/ecdsa" + "encoding/hex" "errors" "github.com/golang/protobuf/proto" @@ -120,6 +121,8 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte } func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { + + p.config.Logger.Debug("handling push notification query") response := &protobuf.PushNotificationQueryResponse{} if query == nil || len(query.PublicKeys) == 0 { return response @@ -240,6 +243,12 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } + if err := s.listenToPublicKeyQueryTopic(common.HashPublicKey(publicKey)); err != nil { + response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR + s.config.Logger.Error("failed to listen to topic", zap.Error(err)) + return response + + } response.Success = true s.config.Logger.Debug("handled push notification registration successfully") @@ -247,6 +256,11 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } +func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { + encodedPublicKey := hex.EncodeToString(hashedPublicKey) + return s.messageProcessor.JoinPublic(encodedPublicKey) +} + func (p *Server) HandlePushNotificationRegistration2(publicKey *ecdsa.PublicKey, payload []byte) error { response := p.HandlePushNotificationRegistration(publicKey, payload) if response == nil { @@ -266,11 +280,12 @@ func (p *Server) HandlePushNotificationRegistration2(publicKey *ecdsa.PublicKey, return err } -func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, query protobuf.PushNotificationQuery) error { +func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, messageID []byte, query protobuf.PushNotificationQuery) error { response := p.HandlePushNotificationQuery(&query) if response == nil { return nil } + response.MessageId = messageID encodedMessage, err := proto.Marshal(response) if err != nil { return err diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index bedcb87f5..37854d05f 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -15,6 +15,7 @@ import ( 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/push_notification_client" "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/whisper/v6" @@ -24,6 +25,8 @@ func TestMessengerPushNotificationSuite(t *testing.T) { suite.Run(t, new(MessengerPushNotificationSuite)) } +// TODO: to test. Register -> stop server -> re-start -> is it loading the topics? + type MessengerPushNotificationSuite struct { suite.Suite m *Messenger // main instance of Messenger @@ -113,19 +116,26 @@ func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Whi func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { errChan := make(chan error) - deviceToken := "token" + bob1DeviceToken := "token-1" + bob2DeviceToken := "token-2" + var bob1AccessTokens, bob2AccessTokens []string + bob1 := s.m + bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) client2 := s.newMessenger(s.shh) - err := s.m.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + // Register bob1 + err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) go func() { - err := s.m.RegisterForPushNotifications(context.Background(), deviceToken) + bob1AccessTokens, err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) errChan <- err }() + // Receive message, reply + // TODO: find a better way to handle this waiting time.Sleep(500 * time.Millisecond) _, err = server.RetrieveAll() s.Require().NoError(err) @@ -138,58 +148,129 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { _, err = server.RetrieveAll() s.Require().NoError(err) + // Check reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + // Make sure we receive it + err = <-errChan + s.Require().NoError(err) + s.Require().NotNil(bob1AccessTokens) + + // Register bob2 + err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + go func() { + bob2AccessTokens, err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) + errChan <- err + }() + + // Receive message, reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + // Check reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + // Make sure we receive it + err = <-errChan + s.Require().NoError(err) + s.Require().NotNil(bob2AccessTokens) + + var info []*push_notification_client.PushNotificationInfo + go func() { + info, err = client2.pushNotificationClient.RetrievePushNotificationInfo(&bob2.identity.PublicKey) + errChan <- err + }() + + // Receive push notification query + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + // Receive push notification query response + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = client2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = client2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = client2.RetrieveAll() + s.Require().NoError(err) + err = <-errChan s.Require().NoError(err) - - info, err := client2.pushNotificationClient.RetrievePushNotificationInfo(&s.m.identity.PublicKey) - s.Require().NoError(err) s.Require().NotNil(info) + // Check we have replies for both bob1 and bob2 + s.Require().Len(info, 2) - /* - s.Require().Len(response.Contacts, 1) - contact := response.Contacts[0] - s.Require().True(contact.IsAdded()) + var bob1Info, bob2Info *push_notification_client.PushNotificationInfo - s.Require().Len(response.Chats, 1) - chat := response.Chats[0] - s.Require().False(chat.Active, "It does not create an active chat") + if info[0].AccessToken == bob1AccessTokens[0] { + bob1Info = info[0] + bob2Info = info[1] + } else { + bob2Info = info[0] + bob1Info = info[1] + } - // Wait for the message to reach its destination - response, err = WaitOnMessengerResponse( - s.m, - func(r *MessengerResponse) bool { return len(r.Contacts) > 0 }, - "contact request not received", - ) - s.Require().NoError(err) + s.Require().NotNil(bob1Info) + s.Require().Equal(bob1Info, &push_notification_client.PushNotificationInfo{ + InstallationID: bob1.installationID, + AccessToken: bob1DeviceToken, + PublicKey: &bob1.identity.PublicKey, + }) - receivedContact := response.Contacts[0] - s.Require().Equal(theirName, receivedContact.Name) - s.Require().Equal(theirPicture, receivedContact.Photo) - s.Require().False(receivedContact.ENSVerified) - s.Require().True(receivedContact.HasBeenAdded()) - s.Require().NotEmpty(receivedContact.LastUpdated) + s.Require().NotNil(bob2Info) + s.Require().Equal(bob2Info, &push_notification_client.PushNotificationInfo{ + InstallationID: bob2.installationID, + AccessToken: bob2DeviceToken, + PublicKey: &bob1.identity.PublicKey, + }) - newPicture := "new-picture" - err = theirMessenger.SendPushNotifications(context.Background(), newName, newPicture) - s.Require().NoError(err) - - // Wait for the message to reach its destination - response, err = WaitOnMessengerResponse( - s.m, - func(r *MessengerResponse) bool { - return len(r.Contacts) > 0 && response.Contacts[0].ID == theirContactID - }, - "contact request not received", - ) - - s.Require().NoError(err) - - receivedContact = response.Contacts[0] - s.Require().Equal(theirContactID, receivedContact.ID) - s.Require().Equal(newName, receivedContact.Name) - s.Require().Equal(newPicture, receivedContact.Photo) - s.Require().False(receivedContact.ENSVerified) - s.Require().True(receivedContact.HasBeenAdded()) - s.Require().NotEmpty(receivedContact.LastUpdated) - */ } From 1c379984cba20f5bce5f4f58a5abe6966ead12e4 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 10 Jul 2020 09:45:40 +0200 Subject: [PATCH 21/46] Handle registration with server --- protocol/messenger.go | 4 +- .../1593601729_initial_schema.up.sql | 1 + .../push_notification_client/persistence.go | 39 ++++++- .../persistence_test.go | 25 ++-- .../push_notification.go | 108 +++++++++++++----- .../push_notification_test.go | 3 +- ...n_server_persistence.go => persistence.go} | 0 ...ersistence_test.go => persistence_test.go} | 0 ...{push_notification_server.go => server.go} | 3 + ...fication_server_test.go => server_test.go} | 2 + protocol/push_notification_test.go | 20 ++-- 11 files changed, 149 insertions(+), 56 deletions(-) rename protocol/push_notification_server/{push_notification_server_persistence.go => persistence.go} (100%) rename protocol/push_notification_server/{push_notification_server_persistence_test.go => persistence_test.go} (100%) rename protocol/push_notification_server/{push_notification_server.go => server.go} (99%) rename protocol/push_notification_server/{push_notification_server_test.go => server_test.go} (99%) diff --git a/protocol/messenger.go b/protocol/messenger.go index e44fbf928..9778bd8f4 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1950,7 +1950,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } logger.Debug("Handling PushNotificationRegistrationResponse") // TODO: Compare DST with Identity - if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil { + if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil { logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err)) } // We continue in any case, no changes to messenger @@ -3021,7 +3021,7 @@ func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ec } // RegisterForPushNotification register deviceToken with any push notification server enabled -func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]string, error) { +func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { if m.pushNotificationClient == nil { return nil, errors.New("push notification client not enabled") } diff --git a/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql index a1efbd827..bbf4ebf71 100644 --- a/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS push_notification_client_servers ( public_key BLOB NOT NULL, registered BOOLEAN DEFAULT FALSE, registered_at INT NOT NULL DEFAULT 0, + access_token TEXT, UNIQUE(public_key) ON CONFLICT REPLACE ); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 7038b2cc8..73a66a06c 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -3,6 +3,7 @@ package push_notification_client import ( "crypto/ecdsa" "database/sql" + "strings" "github.com/status-im/status-go/eth-node/crypto" ) @@ -28,13 +29,13 @@ func (p *Persistence) SentFor(publicKey *ecdsa.PublicKey, messageID []byte) erro } func (p *Persistence) UpsertServer(server *PushNotificationServer) error { - _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at) VALUES (?,?,?)`, crypto.CompressPubkey(server.publicKey), server.registered, server.registeredAt) + _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token) VALUES (?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken) return err } func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { - rows, err := p.db.Query(`SELECT public_key, registered, registered_at FROM push_notification_client_servers`) + rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token FROM push_notification_client_servers`) if err != nil { return nil, err } @@ -42,7 +43,7 @@ func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { for rows.Next() { server := &PushNotificationServer{} var key []byte - err := rows.Scan(&key, &server.registered, &server.registeredAt) + err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken) if err != nil { return nil, err } @@ -50,7 +51,37 @@ func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { if err != nil { return nil, err } - server.publicKey = parsedKey + server.PublicKey = parsedKey + servers = append(servers, server) + } + return servers, nil +} + +func (p *Persistence) GetServersByPublicKey(keys []*ecdsa.PublicKey) ([]*PushNotificationServer, error) { + + keyArgs := make([]interface{}, 0, len(keys)) + for _, key := range keys { + keyArgs = append(keyArgs, crypto.CompressPubkey(key)) + } + + inVector := strings.Repeat("?, ", len(keys)-1) + "?" + rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token FROM push_notification_client_servers WHERE public_key IN (`+inVector+")", keyArgs...) //nolint: gosec + if err != nil { + return nil, err + } + var servers []*PushNotificationServer + for rows.Next() { + server := &PushNotificationServer{} + var key []byte + err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken) + if err != nil { + return nil, err + } + parsedKey, err := crypto.DecompressPubkey(key) + if err != nil { + return nil, err + } + server.PublicKey = parsedKey servers = append(servers, server) } return servers, nil diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 5a6c7a7ee..7c4e55c73 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -39,11 +39,13 @@ func (s *SQLitePersistenceSuite) TearDownTest() { func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { key, err := crypto.GenerateKey() s.Require().NoError(err) + accessToken := "token" server := &PushNotificationServer{ - publicKey: &key.PublicKey, - registered: true, - registeredAt: 1, + PublicKey: &key.PublicKey, + Registered: true, + RegisteredAt: 1, + AccessToken: accessToken, } s.Require().NoError(s.persistence.UpsertServer(server)) @@ -52,12 +54,13 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { s.Require().NoError(err) s.Require().Len(retrievedServers, 1) - s.Require().True(retrievedServers[0].registered) - s.Require().Equal(int64(1), retrievedServers[0].registeredAt) - s.Require().True(common.IsPubKeyEqual(retrievedServers[0].publicKey, &key.PublicKey)) + s.Require().True(retrievedServers[0].Registered) + s.Require().Equal(int64(1), retrievedServers[0].RegisteredAt) + s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey)) + s.Require().Equal(accessToken, retrievedServers[0].AccessToken) - server.registered = false - server.registeredAt = 2 + server.Registered = false + server.RegisteredAt = 2 s.Require().NoError(s.persistence.UpsertServer(server)) @@ -65,7 +68,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { s.Require().NoError(err) s.Require().Len(retrievedServers, 1) - s.Require().False(retrievedServers[0].registered) - s.Require().Equal(int64(2), retrievedServers[0].registeredAt) - s.Require().True(common.IsPubKeyEqual(retrievedServers[0].publicKey, &key.PublicKey)) + s.Require().False(retrievedServers[0].Registered) + s.Require().Equal(int64(2), retrievedServers[0].RegisteredAt) + s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey)) } diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 48b115e45..083a6d9d7 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -26,9 +26,10 @@ const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 type PushNotificationServer struct { - publicKey *ecdsa.PublicKey - registered bool - registeredAt int64 + PublicKey *ecdsa.PublicKey + Registered bool + RegisteredAt int64 + AccessToken string } type PushNotificationInfo struct { @@ -78,21 +79,18 @@ type Client struct { //messageProcessor is a message processor used to send and being notified of messages messageProcessor *common.MessageProcessor - //pushNotificationRegistrationResponses is a channel that listens to pushNotificationResponse - pushNotificationRegistrationResponses chan *protobuf.PushNotificationRegistrationResponse //pushNotificationQueryResponses is a channel that listens to pushNotificationResponse pushNotificationQueryResponses chan *protobuf.PushNotificationQueryResponse } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { return &Client{ - quit: make(chan struct{}), - config: config, - pushNotificationRegistrationResponses: make(chan *protobuf.PushNotificationRegistrationResponse), - pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), - messageProcessor: processor, - persistence: persistence, - reader: rand.Reader} + quit: make(chan struct{}), + config: config, + pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), + messageProcessor: processor, + persistence: persistence, + reader: rand.Reader} } func (c *Client) Start() error { @@ -198,7 +196,7 @@ func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu return options, nil } -func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]string, error) { +func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]*PushNotificationServer, error) { c.DeviceToken = deviceToken servers, err := c.persistence.GetServers() if err != nil { @@ -219,9 +217,21 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut return nil, err } + var serverPublicKeys []*ecdsa.PublicKey for _, server := range servers { - encryptedRegistration, err := c.encryptRegistration(server.publicKey, marshaledRegistration) + // Reset server registration data + server.Registered = false + server.RegisteredAt = 0 + server.AccessToken = registration.AccessToken + serverPublicKeys = append(serverPublicKeys, server.PublicKey) + + if err := c.persistence.UpsertServer(server); err != nil { + return nil, err + } + + // Dispatch message + encryptedRegistration, err := c.encryptRegistration(server.PublicKey, marshaledRegistration) if err != nil { return nil, err } @@ -230,33 +240,71 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION, } - _, err = c.messageProcessor.SendPrivate(context.Background(), server.publicKey, rawMessage) + _, err = c.messageProcessor.SendPrivate(context.Background(), server.PublicKey, rawMessage) - // Send message and wait for reply + if err != nil { + return nil, err + } } - // TODO: this needs to wait for all the registrations, probably best to poll the database + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + + // This code polls the database for server registrations, giving up + // after 5 seconds for { select { case <-c.quit: - return nil, nil - case <-time.After(5 * time.Second): - return nil, errors.New("no registration response received") - case <-c.pushNotificationRegistrationResponses: - return nil, nil + return servers, nil + case <-ctx.Done(): + c.config.Logger.Debug("Context done") + return servers, nil + case <-time.After(200 * time.Millisecond): + servers, err = c.persistence.GetServersByPublicKey(serverPublicKeys) + if err != nil { + return nil, err + } + + allRegistered := true + for _, server := range servers { + allRegistered = allRegistered && server.Registered + } + + // If any of the servers we haven't registered yet, continue + if !allRegistered { + continue + } + + // all have registered,cancel context and return + cancel() + return servers, nil } } } // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database -func (c *Client) HandlePushNotificationRegistrationResponse(response protobuf.PushNotificationRegistrationResponse) error { +func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, response protobuf.PushNotificationRegistrationResponse) error { c.config.Logger.Debug("received push notification registration response", zap.Any("response", response)) - select { - case c.pushNotificationRegistrationResponses <- &response: - default: - return errors.New("could not process push notification registration response") + // TODO: handle non successful response and match request id + // Not successful ignore for now + if !response.Success { + return errors.New("response was not successful") } - return nil + + servers, err := c.persistence.GetServersByPublicKey([]*ecdsa.PublicKey{publicKey}) + if err != nil { + return err + } + // We haven't registered with this server + if len(servers) != 1 { + return errors.New("not registered with this server, ignoring") + } + + server := servers[0] + server.Registered = true + server.RegisteredAt = time.Now().Unix() + + return c.persistence.UpsertServer(server) } // HandlePushNotificationAdvertisement should store any info related to push notifications @@ -289,13 +337,13 @@ func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { } for _, server := range currentServers { - if common.IsPubKeyEqual(server.publicKey, publicKey) { + if common.IsPubKeyEqual(server.PublicKey, publicKey) { return errors.New("push notification server already added") } } return c.persistence.UpsertServer(&PushNotificationServer{ - publicKey: publicKey, + PublicKey: publicKey, }) } diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index 1791fd92e..7487a897d 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/stretchr/testify/require" ) @@ -22,7 +23,7 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { // build chat lish hashes var mutedChatListHashes [][]byte for _, chatID := range mutedChatList { - mutedChatListHashes = append(mutedChatListHashes, shake256(chatID)) + mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) } identity, err := crypto.GenerateKey() diff --git a/protocol/push_notification_server/push_notification_server_persistence.go b/protocol/push_notification_server/persistence.go similarity index 100% rename from protocol/push_notification_server/push_notification_server_persistence.go rename to protocol/push_notification_server/persistence.go diff --git a/protocol/push_notification_server/push_notification_server_persistence_test.go b/protocol/push_notification_server/persistence_test.go similarity index 100% rename from protocol/push_notification_server/push_notification_server_persistence_test.go rename to protocol/push_notification_server/persistence_test.go diff --git a/protocol/push_notification_server/push_notification_server.go b/protocol/push_notification_server/server.go similarity index 99% rename from protocol/push_notification_server/push_notification_server.go rename to protocol/push_notification_server/server.go index be238fb13..223298859 100644 --- a/protocol/push_notification_server/push_notification_server.go +++ b/protocol/push_notification_server/server.go @@ -257,6 +257,9 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, } func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { + if s.messageProcessor == nil { + return nil + } encodedPublicKey := hex.EncodeToString(hashedPublicKey) return s.messageProcessor.JoinPublic(encodedPublicKey) } diff --git a/protocol/push_notification_server/push_notification_server_test.go b/protocol/push_notification_server/server_test.go similarity index 99% rename from protocol/push_notification_server/push_notification_server_test.go rename to protocol/push_notification_server/server_test.go index 420f09d8d..71ec95a10 100644 --- a/protocol/push_notification_server/push_notification_server_test.go +++ b/protocol/push_notification_server/server_test.go @@ -14,6 +14,7 @@ import ( "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" + "github.com/status-im/status-go/protocol/tt" ) func TestServerSuite(t *testing.T) { @@ -55,6 +56,7 @@ func (s *ServerSuite) SetupTest() { config := &Config{ Identity: identity, + Logger: tt.MustCreateTestLogger(), } s.server = New(config, s.persistence, nil) diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 37854d05f..310f81ffb 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -118,7 +118,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { bob1DeviceToken := "token-1" bob2DeviceToken := "token-2" - var bob1AccessTokens, bob2AccessTokens []string + var bob1Servers, bob2Servers []*push_notification_client.PushNotificationServer bob1 := s.m bob2 := s.newMessengerWithKey(s.shh, s.m.identity) @@ -130,7 +130,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NoError(err) go func() { - bob1AccessTokens, err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) + bob1Servers, err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) errChan <- err }() @@ -165,14 +165,16 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { // Make sure we receive it err = <-errChan s.Require().NoError(err) - s.Require().NotNil(bob1AccessTokens) + s.Require().NotNil(bob1Servers) + s.Require().Len(bob1Servers, 1) + s.Require().True(bob1Servers[0].Registered) // Register bob2 err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) go func() { - bob2AccessTokens, err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) + bob2Servers, err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) errChan <- err }() @@ -207,7 +209,9 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { // Make sure we receive it err = <-errChan s.Require().NoError(err) - s.Require().NotNil(bob2AccessTokens) + s.Require().NotNil(bob2Servers) + s.Require().Len(bob2Servers, 1) + s.Require().True(bob2Servers[0].Registered) var info []*push_notification_client.PushNotificationInfo go func() { @@ -251,7 +255,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { var bob1Info, bob2Info *push_notification_client.PushNotificationInfo - if info[0].AccessToken == bob1AccessTokens[0] { + if info[0].AccessToken == bob1Servers[0].AccessToken { bob1Info = info[0] bob2Info = info[1] } else { @@ -262,14 +266,14 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NotNil(bob1Info) s.Require().Equal(bob1Info, &push_notification_client.PushNotificationInfo{ InstallationID: bob1.installationID, - AccessToken: bob1DeviceToken, + AccessToken: bob1Servers[0].AccessToken, PublicKey: &bob1.identity.PublicKey, }) s.Require().NotNil(bob2Info) s.Require().Equal(bob2Info, &push_notification_client.PushNotificationInfo{ InstallationID: bob2.installationID, - AccessToken: bob2DeviceToken, + AccessToken: bob2Servers[0].AccessToken, PublicKey: &bob1.identity.PublicKey, }) From 20fb8607cbe11d54dc75dce5194c1696a233cc16 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 10 Jul 2020 15:26:06 +0200 Subject: [PATCH 22/46] keep track of queries & actually send pns, hurray --- protocol/common/message_processor.go | 1 + protocol/messenger.go | 17 +- protocol/protobuf/push_notifications.pb.go | 130 ++++----- protocol/protobuf/push_notifications.proto | 6 +- .../1593601729_initial_schema.up.sql | 16 -- .../1593601729_initial_schema.down.sql | 0 .../sql/1593601729_initial_schema.up.sql | 40 +++ .../migrations/{ => sql}/doc.go | 2 +- .../push_notification_client/persistence.go | 122 +++++++- .../persistence_test.go | 66 +++++ .../push_notification.go | 262 +++++++++++++----- .../push_notification_test.go | 119 ++++++-- .../1593601728_initial_schema.down.sql | 0 .../1593601728_initial_schema.up.sql | 0 .../migrations/{ => sql}/doc.go | 2 +- .../push_notification_server/persistence.go | 22 ++ protocol/push_notification_server/server.go | 15 + protocol/push_notification_test.go | 43 +-- 18 files changed, 655 insertions(+), 208 deletions(-) delete mode 100644 protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql rename protocol/push_notification_client/migrations/{ => sql}/1593601729_initial_schema.down.sql (100%) create mode 100644 protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql rename protocol/push_notification_client/migrations/{ => sql}/doc.go (83%) rename protocol/push_notification_server/migrations/{ => sql}/1593601728_initial_schema.down.sql (100%) rename protocol/push_notification_server/migrations/{ => sql}/1593601728_initial_schema.up.sql (100%) rename protocol/push_notification_server/migrations/{ => sql}/doc.go (83%) diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index eaa6246e8..495e6f715 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -388,6 +388,7 @@ func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []b // sendDataSync sends a message scheduled by the data sync layer. // Data Sync layer calls this method "dispatch" function. func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error { + // Calculate the messageIDs messageIDs := make([][]byte, 0, len(payload.Messages)) for _, payload := range payload.Messages { messageIDs = append(messageIDs, v1protocol.MessageID(&p.identity.PublicKey, payload.Body)) diff --git a/protocol/messenger.go b/protocol/messenger.go index 9778bd8f4..5a8378f1a 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -287,6 +287,7 @@ func NewMessenger( verifyTransactionClient: c.verifyTransactionClient, shutdownTasks: []func() error{ database.Close, + pushNotificationClient.Stop, transp.ResetFilters, transp.Stop, func() error { processor.Stop(); return nil }, @@ -303,6 +304,18 @@ func NewMessenger( } func (m *Messenger) Start() error { + // Start push notification server + if m.pushNotificationServer != nil { + if err := m.pushNotificationServer.Start(); err != nil { + return err + } + } + if m.pushNotificationClient != nil { + if err := m.pushNotificationClient.Start(); err != nil { + return err + } + } + return m.encryptor.Start(m.identity) } @@ -1394,7 +1407,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes // If the chat is not public, we instruct the pushNotificationService to send a notification if !chat.Public() && m.pushNotificationClient != nil { - if err := m.pushNotificationClient.NotifyOnMessageID(id); err != nil { + if err := m.pushNotificationClient.NotifyOnMessageID(chat.ID, id); err != nil { return nil, err } @@ -1963,7 +1976,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } logger.Debug("Handling PushNotificationQueryResponse") // TODO: Compare DST with Identity - if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil { + if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil { logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err)) } // We continue in any case, no changes to messenger diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 12dc55a40..c59a9016a 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -595,8 +595,7 @@ func (m *PushNotification) GetMessage() []byte { type PushNotificationRequest struct { Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"` - MessageId string `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` - AckRequired string `protobuf:"bytes,3,opt,name=ack_required,json=ackRequired,proto3" json:"ack_required,omitempty"` + MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -634,18 +633,11 @@ func (m *PushNotificationRequest) GetRequests() []*PushNotification { return nil } -func (m *PushNotificationRequest) GetMessageId() string { +func (m *PushNotificationRequest) GetMessageId() []byte { if m != nil { return m.MessageId } - return "" -} - -func (m *PushNotificationRequest) GetAckRequired() string { - if m != nil { - return m.AckRequired - } - return "" + return nil } type PushNotificationReport struct { @@ -711,9 +703,8 @@ func (m *PushNotificationReport) GetInstallationId() string { return "" } -// TOP LEVEL type PushNotificationResponse struct { - MessageId string `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` + MessageId []byte `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` Reports []*PushNotificationReport `protobuf:"bytes,2,rep,name=reports,proto3" json:"reports,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -745,11 +736,11 @@ func (m *PushNotificationResponse) XXX_DiscardUnknown() { var xxx_messageInfo_PushNotificationResponse proto.InternalMessageInfo -func (m *PushNotificationResponse) GetMessageId() string { +func (m *PushNotificationResponse) GetMessageId() []byte { if m != nil { return m.MessageId } - return "" + return nil } func (m *PushNotificationResponse) GetReports() []*PushNotificationReport { @@ -779,59 +770,58 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 857 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0x51, 0x6f, 0xe3, 0x44, - 0x10, 0xc7, 0x59, 0x27, 0x6d, 0xe2, 0x49, 0x68, 0x73, 0xab, 0x5e, 0xcf, 0x9c, 0x38, 0xc8, 0x19, - 0x24, 0xa2, 0x43, 0x8a, 0x50, 0x91, 0xe0, 0xc4, 0x13, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x1b, - 0x97, 0x13, 0x12, 0x92, 0xe5, 0xc4, 0xdb, 0x8b, 0x95, 0x9c, 0x1d, 0x76, 0xd7, 0x87, 0xf2, 0x80, - 0xc4, 0x27, 0x40, 0x42, 0xbc, 0xf1, 0x29, 0xd0, 0x7d, 0x07, 0xbe, 0x17, 0xf2, 0xda, 0x0e, 0x8e, - 0xe3, 0x4b, 0xf3, 0x70, 0x4f, 0xf1, 0xfc, 0x77, 0x66, 0x77, 0xe7, 0x37, 0x3b, 0x13, 0xd0, 0x96, - 0x31, 0x9f, 0xb9, 0x61, 0x24, 0x82, 0xbb, 0x60, 0xea, 0x89, 0x20, 0x0a, 0x79, 0x7f, 0xc9, 0x22, - 0x11, 0xe1, 0xa6, 0xfc, 0x99, 0xc4, 0x77, 0xfa, 0xbf, 0x35, 0xf8, 0x70, 0x14, 0xf3, 0x99, 0x55, - 0xf0, 0x22, 0xf4, 0x65, 0xc0, 0x05, 0x93, 0xdf, 0xd8, 0x06, 0x10, 0xd1, 0x9c, 0x86, 0xae, 0x58, - 0x2d, 0xa9, 0x86, 0xba, 0xa8, 0x77, 0x74, 0xf6, 0x45, 0x3f, 0x8f, 0xef, 0xef, 0x8a, 0xed, 0x3b, - 0x49, 0xa0, 0xb3, 0x5a, 0x52, 0xa2, 0x8a, 0xfc, 0x13, 0x9f, 0xc0, 0x81, 0x34, 0x34, 0xa5, 0x8b, - 0x7a, 0x2a, 0x49, 0x0d, 0xfc, 0x19, 0x1c, 0x07, 0x21, 0x17, 0xde, 0x62, 0x21, 0x43, 0xdd, 0xc0, - 0xd7, 0x6a, 0x72, 0xfd, 0xa8, 0x28, 0x9b, 0x3e, 0x7e, 0x0a, 0x6d, 0x6f, 0x3a, 0xa5, 0x9c, 0xbb, - 0xe9, 0x2e, 0x75, 0xe9, 0xd5, 0x4a, 0x35, 0x79, 0x20, 0xd6, 0xa0, 0x41, 0x43, 0x6f, 0xb2, 0xa0, - 0xbe, 0x76, 0xd0, 0x45, 0xbd, 0x26, 0xc9, 0xcd, 0x64, 0xe5, 0x35, 0x65, 0x3c, 0x88, 0x42, 0xed, - 0xb0, 0x8b, 0x7a, 0x75, 0x92, 0x9b, 0xf8, 0x19, 0x3c, 0xf0, 0x16, 0x8b, 0xe8, 0x57, 0xea, 0xbb, - 0x31, 0xa7, 0xcc, 0x5d, 0x04, 0x5c, 0x68, 0x8d, 0x6e, 0xad, 0xd7, 0x26, 0xc7, 0xd9, 0xc2, 0x2d, - 0xa7, 0xec, 0x26, 0xe0, 0x22, 0xf1, 0x9d, 0x2c, 0xa2, 0xe9, 0x9c, 0xfa, 0xee, 0x74, 0xe6, 0x89, - 0xd4, 0xb7, 0x99, 0xfa, 0x66, 0x0b, 0xe7, 0x33, 0x4f, 0x48, 0xdf, 0x8f, 0x00, 0xe2, 0x90, 0x49, - 0x28, 0x94, 0x69, 0xaa, 0xbc, 0x4e, 0x41, 0xd1, 0x2f, 0x41, 0x5d, 0x53, 0xc2, 0xa7, 0x80, 0x6f, - 0xad, 0x6b, 0xcb, 0x7e, 0x61, 0xb9, 0x8e, 0x7d, 0x6d, 0x58, 0xae, 0xf3, 0xd3, 0xc8, 0xe8, 0xbc, - 0x87, 0xdf, 0x07, 0x75, 0x30, 0xca, 0xb4, 0x0e, 0xc2, 0x18, 0x8e, 0x2e, 0x4d, 0x62, 0x7c, 0x37, - 0x18, 0x1b, 0x99, 0xa6, 0xe8, 0x6f, 0x14, 0xf8, 0x74, 0x57, 0x2d, 0x08, 0xe5, 0xcb, 0x28, 0xe4, - 0x34, 0x41, 0xc0, 0x63, 0x09, 0x4b, 0x16, 0xb3, 0x49, 0x72, 0x13, 0x5b, 0x70, 0x40, 0x19, 0x8b, - 0x98, 0x2c, 0xcc, 0xd1, 0xd9, 0xf3, 0xfd, 0x8a, 0x9c, 0x6f, 0xdc, 0x37, 0x92, 0x58, 0x59, 0xec, - 0x74, 0x1b, 0xfc, 0x04, 0x80, 0xd1, 0x5f, 0x62, 0xca, 0x45, 0x5e, 0xcd, 0x36, 0x51, 0x33, 0xc5, - 0xf4, 0xf5, 0xdf, 0x11, 0xa8, 0xeb, 0x98, 0x62, 0xea, 0x06, 0x21, 0x36, 0xc9, 0x53, 0x7f, 0x08, - 0x0f, 0x86, 0x83, 0x9b, 0x4b, 0x9b, 0x0c, 0x8d, 0x0b, 0x77, 0x68, 0x8c, 0xc7, 0x83, 0x2b, 0xa3, - 0x83, 0xf0, 0x09, 0x74, 0x7e, 0x34, 0xc8, 0xd8, 0xb4, 0x2d, 0x77, 0x68, 0x8e, 0x87, 0x03, 0xe7, - 0xfc, 0xfb, 0x8e, 0x82, 0x1f, 0xc3, 0xe9, 0xad, 0x35, 0xbe, 0x1d, 0x8d, 0x6c, 0xe2, 0x18, 0x17, - 0x45, 0x86, 0xb5, 0x04, 0x9a, 0x69, 0x39, 0x06, 0xb1, 0x06, 0x37, 0xe9, 0x09, 0x9d, 0xba, 0xfe, - 0x07, 0x82, 0xa7, 0xe5, 0xdc, 0x06, 0xfe, 0x6b, 0xca, 0x44, 0xc0, 0xe9, 0x2b, 0x1a, 0x0a, 0x33, - 0xbc, 0x8b, 0x92, 0x3c, 0x96, 0xf1, 0x64, 0x11, 0x4c, 0xdd, 0x39, 0x5d, 0x49, 0x68, 0x6d, 0xa2, - 0xa6, 0xca, 0x35, 0x5d, 0x6d, 0x3d, 0x48, 0x65, 0xfb, 0x41, 0xee, 0xfb, 0xb8, 0xf5, 0xdf, 0x40, - 0x3b, 0x8f, 0x42, 0xe1, 0x4d, 0xc5, 0x79, 0xe4, 0xd3, 0x8d, 0xab, 0x60, 0x0f, 0x4e, 0xb7, 0xfa, - 0xd9, 0x0d, 0xc2, 0xbb, 0x48, 0x43, 0xdd, 0x5a, 0xaf, 0x75, 0xf6, 0xf9, 0xdb, 0xeb, 0xb5, 0x95, - 0x13, 0x39, 0x59, 0x96, 0x5c, 0x12, 0x55, 0x7f, 0x0e, 0x0f, 0xcb, 0xa1, 0x3f, 0xc4, 0x94, 0xad, - 0xf0, 0xc7, 0xd0, 0xfa, 0x1f, 0x01, 0x97, 0x07, 0xb6, 0x09, 0xac, 0x19, 0x70, 0xfd, 0x0d, 0x82, - 0x0f, 0x2a, 0x43, 0x25, 0xc1, 0x32, 0x22, 0xb4, 0x17, 0x22, 0xa5, 0xb2, 0xff, 0x37, 0xab, 0x51, - 0x2b, 0x57, 0xa3, 0xb2, 0x8f, 0xeb, 0x95, 0x7d, 0xac, 0xff, 0x89, 0xe0, 0x49, 0xe5, 0xa5, 0xd7, - 0xcd, 0xf2, 0x35, 0xd4, 0x0b, 0x84, 0x3f, 0x79, 0x3b, 0xe1, 0x75, 0xae, 0x44, 0x06, 0x24, 0xb7, - 0x7c, 0x45, 0x39, 0xf7, 0x5e, 0xd2, 0x3c, 0x93, 0x36, 0x51, 0x33, 0xc5, 0xf4, 0x8b, 0x4d, 0x58, - 0xdb, 0x68, 0x42, 0xfd, 0x1f, 0x04, 0x9d, 0xf2, 0xe6, 0xfb, 0xf0, 0x7b, 0x04, 0x0d, 0x39, 0x8b, - 0xd6, 0xdc, 0x0e, 0x13, 0xf3, 0x7e, 0x5e, 0x15, 0xdc, 0xeb, 0x95, 0xdc, 0x35, 0x68, 0x64, 0xf7, - 0x97, 0x43, 0xb5, 0x4d, 0x72, 0x53, 0xff, 0x0b, 0xc1, 0xa3, 0xed, 0x09, 0x21, 0xdb, 0x1c, 0x7f, - 0x05, 0xcd, 0xac, 0xe3, 0x79, 0x06, 0xf1, 0xf1, 0x8e, 0xb1, 0xb2, 0xf6, 0xad, 0xe0, 0xa7, 0x16, - 0xf9, 0x49, 0x20, 0x73, 0x37, 0x71, 0x0f, 0x18, 0xcd, 0xbb, 0xa9, 0xe5, 0x4d, 0xe7, 0x24, 0x93, - 0xf4, 0xbf, 0x15, 0x38, 0xdd, 0xbe, 0xd5, 0x32, 0x62, 0x62, 0xc7, 0x08, 0xfc, 0x76, 0x73, 0x04, - 0x3e, 0xdb, 0x35, 0x02, 0x93, 0xad, 0x2a, 0x87, 0xde, 0xbb, 0xc0, 0xad, 0xff, 0xbc, 0xcf, 0x70, - 0x3c, 0x86, 0xd6, 0x0b, 0x62, 0x5b, 0x57, 0xc5, 0x7f, 0x86, 0xd2, 0x90, 0x53, 0x12, 0xcd, 0xb2, - 0x1d, 0x97, 0x18, 0x57, 0xe6, 0xd8, 0x31, 0x88, 0x71, 0xd1, 0xa9, 0xe9, 0x31, 0x68, 0xdb, 0x09, - 0x65, 0x6f, 0x7e, 0x13, 0x3d, 0x2a, 0xa3, 0xff, 0x06, 0x1a, 0x4c, 0xe6, 0xce, 0x35, 0x45, 0x16, - 0xb4, 0x7b, 0x1f, 0x24, 0x92, 0x07, 0x4c, 0x0e, 0xa5, 0xe7, 0x97, 0xff, 0x05, 0x00, 0x00, 0xff, - 0xff, 0x8b, 0x53, 0x27, 0x32, 0x99, 0x08, 0x00, 0x00, + // 836 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x51, 0x6f, 0xe3, 0x44, + 0x10, 0xc7, 0xb1, 0x9d, 0x36, 0xf1, 0x34, 0xb4, 0xbe, 0x55, 0xaf, 0x67, 0x4e, 0x1c, 0xe4, 0x0c, + 0x12, 0xd1, 0x21, 0x45, 0xa8, 0x48, 0x70, 0xe2, 0x89, 0xd0, 0xba, 0xc5, 0x6a, 0x63, 0x87, 0x8d, + 0xc3, 0x09, 0x09, 0xc9, 0x72, 0xe2, 0xcd, 0xc5, 0xaa, 0xcf, 0x36, 0xde, 0xf5, 0xa1, 0x3c, 0x20, + 0xf1, 0x09, 0x90, 0x78, 0xe5, 0x53, 0xa0, 0xfb, 0x0e, 0x7c, 0x2f, 0xe4, 0xb5, 0x1d, 0x1c, 0xc7, + 0x4d, 0xf3, 0xc0, 0x93, 0x3d, 0xb3, 0x33, 0xbb, 0x3b, 0xbf, 0xd9, 0xf9, 0x83, 0x1a, 0xa7, 0x74, + 0xe9, 0x84, 0x11, 0xf3, 0x17, 0xfe, 0xdc, 0x65, 0x7e, 0x14, 0xd2, 0x41, 0x9c, 0x44, 0x2c, 0x42, + 0x1d, 0xfe, 0x99, 0xa5, 0x0b, 0xed, 0x1f, 0x09, 0x3e, 0x1c, 0xa7, 0x74, 0x69, 0x56, 0xa2, 0x30, + 0x79, 0xed, 0x53, 0x96, 0xf0, 0x7f, 0x64, 0x01, 0xb0, 0xe8, 0x8e, 0x84, 0x0e, 0x5b, 0xc5, 0x44, + 0x15, 0x7a, 0x42, 0xff, 0xf8, 0xfc, 0x8b, 0x41, 0x99, 0x3f, 0xd8, 0x95, 0x3b, 0xb0, 0xb3, 0x44, + 0x7b, 0x15, 0x13, 0x2c, 0xb3, 0xf2, 0x17, 0x9d, 0xc2, 0x01, 0x37, 0x54, 0xb1, 0x27, 0xf4, 0x65, + 0x9c, 0x1b, 0xe8, 0x33, 0x38, 0xf1, 0x43, 0xca, 0xdc, 0x20, 0xe0, 0xa9, 0x8e, 0xef, 0xa9, 0x12, + 0x5f, 0x3f, 0xae, 0xba, 0x0d, 0x0f, 0x3d, 0x87, 0xae, 0x3b, 0x9f, 0x13, 0x4a, 0x9d, 0x7c, 0x97, + 0x16, 0x8f, 0x3a, 0xca, 0x7d, 0xfc, 0x40, 0xa4, 0x42, 0x9b, 0x84, 0xee, 0x2c, 0x20, 0x9e, 0x7a, + 0xd0, 0x13, 0xfa, 0x1d, 0x5c, 0x9a, 0xd9, 0xca, 0x5b, 0x92, 0x50, 0x3f, 0x0a, 0xd5, 0xc3, 0x9e, + 0xd0, 0x6f, 0xe1, 0xd2, 0x44, 0x2f, 0xe0, 0x91, 0x1b, 0x04, 0xd1, 0xaf, 0xc4, 0x73, 0x52, 0x4a, + 0x12, 0x27, 0xf0, 0x29, 0x53, 0xdb, 0x3d, 0xa9, 0xdf, 0xc5, 0x27, 0xc5, 0xc2, 0x94, 0x92, 0xe4, + 0xd6, 0xa7, 0x2c, 0x8b, 0x9d, 0x05, 0xd1, 0xfc, 0x8e, 0x78, 0xce, 0x7c, 0xe9, 0xb2, 0x3c, 0xb6, + 0x93, 0xc7, 0x16, 0x0b, 0x17, 0x4b, 0x97, 0xf1, 0xd8, 0x8f, 0x00, 0xd2, 0x30, 0xe1, 0x50, 0x48, + 0xa2, 0xca, 0xfc, 0x3a, 0x15, 0x8f, 0x76, 0x05, 0xf2, 0x9a, 0x12, 0x3a, 0x03, 0x34, 0x35, 0x6f, + 0x4c, 0xeb, 0x95, 0xe9, 0xd8, 0xd6, 0x8d, 0x6e, 0x3a, 0xf6, 0x4f, 0x63, 0x5d, 0x79, 0x0f, 0xbd, + 0x0f, 0xf2, 0x70, 0x5c, 0xf8, 0x14, 0x01, 0x21, 0x38, 0xbe, 0x32, 0xb0, 0xfe, 0xdd, 0x70, 0xa2, + 0x17, 0x3e, 0x51, 0x7b, 0x27, 0xc2, 0xa7, 0xbb, 0x7a, 0x81, 0x09, 0x8d, 0xa3, 0x90, 0x92, 0x0c, + 0x01, 0x4d, 0x39, 0x2c, 0xde, 0xcc, 0x0e, 0x2e, 0x4d, 0x64, 0xc2, 0x01, 0x49, 0x92, 0x28, 0xe1, + 0x8d, 0x39, 0x3e, 0x7f, 0xb9, 0x5f, 0x93, 0xcb, 0x8d, 0x07, 0x7a, 0x96, 0xcb, 0x9b, 0x9d, 0x6f, + 0x83, 0x9e, 0x01, 0x24, 0xe4, 0x97, 0x94, 0x50, 0x56, 0x76, 0xb3, 0x8b, 0xe5, 0xc2, 0x63, 0x78, + 0xda, 0xef, 0x02, 0xc8, 0xeb, 0x9c, 0x6a, 0xe9, 0x3a, 0xc6, 0x16, 0x2e, 0x4b, 0x7f, 0x0c, 0x8f, + 0x46, 0xc3, 0xdb, 0x2b, 0x0b, 0x8f, 0xf4, 0x4b, 0x67, 0xa4, 0x4f, 0x26, 0xc3, 0x6b, 0x5d, 0x11, + 0xd0, 0x29, 0x28, 0x3f, 0xea, 0x78, 0x62, 0x58, 0xa6, 0x33, 0x32, 0x26, 0xa3, 0xa1, 0x7d, 0xf1, + 0xbd, 0x22, 0xa2, 0xa7, 0x70, 0x36, 0x35, 0x27, 0xd3, 0xf1, 0xd8, 0xc2, 0xb6, 0x7e, 0x59, 0x65, + 0x28, 0x65, 0xd0, 0x0c, 0xd3, 0xd6, 0xb1, 0x39, 0xbc, 0xcd, 0x4f, 0x50, 0x5a, 0xda, 0x1f, 0x02, + 0x3c, 0xaf, 0xd7, 0x36, 0xf4, 0xde, 0x92, 0x84, 0xf9, 0x94, 0xbc, 0x21, 0x21, 0x33, 0xc2, 0x45, + 0x94, 0xd5, 0x11, 0xa7, 0xb3, 0xc0, 0x9f, 0x3b, 0x77, 0x64, 0xc5, 0xa1, 0x75, 0xb1, 0x9c, 0x7b, + 0x6e, 0xc8, 0x6a, 0xeb, 0x41, 0x8a, 0xdb, 0x0f, 0x72, 0xdf, 0xc7, 0xad, 0xfd, 0x06, 0xea, 0x45, + 0x14, 0x32, 0x77, 0xce, 0x2e, 0x22, 0x8f, 0x6c, 0x5c, 0x05, 0xb9, 0x70, 0xb6, 0x35, 0xcf, 0x8e, + 0x1f, 0x2e, 0x22, 0x55, 0xe8, 0x49, 0xfd, 0xa3, 0xf3, 0xcf, 0xef, 0xef, 0xd7, 0x56, 0x4d, 0xf8, + 0x34, 0xae, 0x85, 0x64, 0x5e, 0xed, 0x25, 0x3c, 0xae, 0xa7, 0xfe, 0x90, 0x92, 0x64, 0x85, 0x3e, + 0x86, 0xa3, 0xff, 0x10, 0x50, 0x7e, 0x60, 0x17, 0xc3, 0x9a, 0x01, 0xd5, 0xde, 0x09, 0xf0, 0x41, + 0x63, 0x2a, 0x27, 0x58, 0x47, 0x24, 0xec, 0x85, 0x48, 0x6c, 0x9c, 0xff, 0xcd, 0x6e, 0x48, 0xf5, + 0x6e, 0x34, 0xce, 0x71, 0xab, 0x71, 0x8e, 0xb5, 0x3f, 0x05, 0x78, 0xd6, 0x78, 0xe9, 0xf5, 0xb0, + 0x7c, 0x0d, 0xad, 0x0a, 0xe1, 0x4f, 0xee, 0x27, 0xbc, 0xae, 0x15, 0xf3, 0x84, 0xec, 0x96, 0x6f, + 0x08, 0xa5, 0xee, 0x6b, 0x52, 0x56, 0xd2, 0xc5, 0x72, 0xe1, 0x31, 0xbc, 0xea, 0x10, 0x4a, 0x1b, + 0x43, 0xa8, 0xfd, 0x2d, 0x80, 0x52, 0xdf, 0x7c, 0x1f, 0x7e, 0x4f, 0xa0, 0xcd, 0xb5, 0x68, 0xcd, + 0xed, 0x30, 0x33, 0x1f, 0xe6, 0xd5, 0xc0, 0xbd, 0xd5, 0xc8, 0x5d, 0x85, 0x76, 0x71, 0x7f, 0x2e, + 0xaa, 0x5d, 0x5c, 0x9a, 0x5a, 0x0c, 0x4f, 0xb6, 0x05, 0x82, 0x4f, 0x39, 0xfa, 0x0a, 0x3a, 0xc5, + 0xc0, 0xd3, 0x82, 0xe1, 0xd3, 0x1d, 0xaa, 0xb2, 0x8e, 0x7d, 0x00, 0x9f, 0xf6, 0x97, 0x08, 0x67, + 0xdb, 0x47, 0xc6, 0x51, 0xc2, 0x76, 0xc8, 0xdb, 0xb7, 0x9b, 0xf2, 0xf6, 0x62, 0x97, 0xbc, 0x65, + 0x5b, 0x35, 0x0a, 0xda, 0xff, 0x81, 0x52, 0xfb, 0x79, 0x1f, 0xe1, 0x3b, 0x81, 0xa3, 0x57, 0xd8, + 0x32, 0xaf, 0xab, 0xaa, 0x5f, 0x13, 0x30, 0x31, 0xf3, 0x99, 0x96, 0xed, 0x60, 0xfd, 0xda, 0x98, + 0xd8, 0x3a, 0xd6, 0x2f, 0x15, 0x49, 0x4b, 0x41, 0xdd, 0x2e, 0xa8, 0x78, 0xcf, 0x9b, 0x5c, 0x85, + 0xfa, 0xb3, 0xfc, 0x06, 0xda, 0x09, 0xaf, 0x9d, 0xaa, 0x22, 0xef, 0x56, 0xef, 0x21, 0x48, 0xb8, + 0x4c, 0x98, 0x1d, 0xf2, 0xc8, 0x2f, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x28, 0xe3, 0xa0, + 0x75, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index fbcd74dc2..c8342ebcd 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -70,8 +70,7 @@ message PushNotification { message PushNotificationRequest { repeated PushNotification requests = 1; - string message_id = 2; - string ack_required = 3; + bytes message_id = 2; } message PushNotificationReport { @@ -87,8 +86,7 @@ message PushNotificationReport { string installation_id = 4; } -// TOP LEVEL message PushNotificationResponse { - string message_id = 1; + bytes message_id = 1; repeated PushNotificationReport reports = 2; } diff --git a/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql deleted file mode 100644 index bbf4ebf71..000000000 --- a/protocol/push_notification_client/migrations/1593601729_initial_schema.up.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE TABLE IF NOT EXISTS push_notification_client_servers ( - public_key BLOB NOT NULL, - registered BOOLEAN DEFAULT FALSE, - registered_at INT NOT NULL DEFAULT 0, - access_token TEXT, - UNIQUE(public_key) ON CONFLICT REPLACE -); - -CREATE TABLE IF NOT EXISTS push_notification_client_info ( - public_key BLOB NOT NULL, - installation_id TEXT NOT NULL, - access_token TEXT NOT NULL, - UNIQUE(public_key, installation_id) ON CONFLICT REPLACE -); - -CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id); diff --git a/protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.down.sql similarity index 100% rename from protocol/push_notification_client/migrations/1593601729_initial_schema.down.sql rename to protocol/push_notification_client/migrations/sql/1593601729_initial_schema.down.sql diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql new file mode 100644 index 000000000..afb671961 --- /dev/null +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -0,0 +1,40 @@ +CREATE TABLE IF NOT EXISTS push_notification_client_servers ( + public_key BLOB NOT NULL, + registered BOOLEAN DEFAULT FALSE, + registered_at INT NOT NULL DEFAULT 0, + access_token TEXT, + UNIQUE(public_key) ON CONFLICT REPLACE +); + +CREATE TABLE IF NOT EXISTS push_notification_client_queries ( + public_key BLOB NOT NULL, + queried_at INT NOT NULL, + query_id BLOB NOT NULL, + UNIQUE(public_key,query_id) ON CONFLICT REPLACE +); + +CREATE TABLE IF NOT EXISTS push_notification_client_info ( + public_key BLOB NOT NULL, + server_public_key BLOB NOT NULL, + installation_id TEXT NOT NULL, + access_token TEXT NOT NULL, + retrieved_at INT NOT NULL, + UNIQUE(public_key, installation_id, server_public_key) ON CONFLICT REPLACE +); + +CREATE TABLE IF NOT EXISTS push_notification_client_tracked_messages ( + message_id BLOB NOT NULL, + chat_id TEXT NOT NULL, + tracked_at INT NOT NULL, + UNIQUE(message_id) ON CONFLICT IGNORE + ); + +CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( + message_id BLOB NOT NULL, + public_key BLOB NOT NULL, + installation_id TEXT NOT NULL, + sent_at INT NOT NULL, + UNIQUE(message_id, public_key, installation_id) + ); + +CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id); diff --git a/protocol/push_notification_client/migrations/doc.go b/protocol/push_notification_client/migrations/sql/doc.go similarity index 83% rename from protocol/push_notification_client/migrations/doc.go rename to protocol/push_notification_client/migrations/sql/doc.go index 0315ccce1..a7d080561 100644 --- a/protocol/push_notification_client/migrations/doc.go +++ b/protocol/push_notification_client/migrations/sql/doc.go @@ -6,4 +6,4 @@ package migrations -//go:generate go-bindata -pkg migrations -o ./migrations.go . +//go:generate go-bindata -pkg migrations -o ../migrations.go ./ diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 73a66a06c..a4ca30a90 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -1,9 +1,11 @@ package push_notification_client import ( + "context" "crypto/ecdsa" "database/sql" "strings" + "time" "github.com/status-im/status-go/eth-node/crypto" ) @@ -16,16 +18,126 @@ func NewPersistence(db *sql.DB) *Persistence { return &Persistence{db: db} } -func (p *Persistence) TrackPushNotification(messageID []byte) error { +func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) error { + trackedAt := time.Now().Unix() + _, err := p.db.Exec(`INSERT INTO push_notification_client_tracked_messages (chat_id, message_id, tracked_at) VALUES (?,?,?)`, chatID, messageID, trackedAt) + return err +} + +func (p *Persistence) SavePushNotificationQuery(publicKey *ecdsa.PublicKey, queryID []byte) error { + queriedAt := time.Now().Unix() + _, err := p.db.Exec(`INSERT INTO push_notification_client_queries (public_key, query_id, queried_at) VALUES (?,?,?)`, crypto.CompressPubkey(publicKey), queryID, queriedAt) + return err +} + +func (p *Persistence) GetQueriedAt(publicKey *ecdsa.PublicKey) (int64, error) { + var queriedAt int64 + err := p.db.QueryRow(`SELECT queried_at FROM push_notification_client_queries WHERE public_key = ? ORDER BY queried_at DESC LIMIT 1`, crypto.CompressPubkey(publicKey)).Scan(&queriedAt) + if err == sql.ErrNoRows { + return 0, nil + } + if err != nil { + return 0, err + } + + return queriedAt, nil +} + +func (p *Persistence) GetQueryPublicKey(queryID []byte) (*ecdsa.PublicKey, error) { + var publicKeyBytes []byte + err := p.db.QueryRow(`SELECT public_key FROM push_notification_client_queries WHERE query_id = ?`, queryID).Scan(&publicKeyBytes) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + publicKey, err := crypto.DecompressPubkey(publicKeyBytes) + if err != nil { + return nil, err + } + return publicKey, nil +} + +func (p *Persistence) SavePushNotificationInfo(infos []*PushNotificationInfo) error { + tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{}) + defer func() { + if err == nil { + err = tx.Commit() + return + } + // don't shadow original error + _ = tx.Rollback() + }() + for _, info := range infos { + _, err := tx.Exec(`INSERT INTO push_notification_client_info (public_key, server_public_key, installation_id, access_token, retrieved_at) VALUES (?, ?, ?, ?, ?)`, crypto.CompressPubkey(info.PublicKey), crypto.CompressPubkey(info.ServerPublicKey), info.InstallationID, info.AccessToken, info.RetrievedAt) + if err != nil { + return err + } + } + return nil } -func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { - return false, nil +func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) { + queryArgs := make([]interface{}, 0, len(installationIDs)+1) + queryArgs = append(queryArgs, crypto.CompressPubkey(publicKey)) + for _, installationID := range installationIDs { + queryArgs = append(queryArgs, installationID) + } + + inVector := strings.Repeat("?, ", len(installationIDs)-1) + "?" + + rows, err := p.db.Query(`SELECT server_public_key, installation_id, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) + if err != nil { + return nil, err + } + var infos []*PushNotificationInfo + for rows.Next() { + var serverPublicKeyBytes []byte + info := &PushNotificationInfo{PublicKey: publicKey} + err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.AccessToken, &info.RetrievedAt) + if err != nil { + return nil, err + } + + serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes) + if err != nil { + return nil, err + } + + info.ServerPublicKey = serverPublicKey + infos = append(infos, info) + } + + return infos, nil } -func (p *Persistence) SentFor(publicKey *ecdsa.PublicKey, messageID []byte) error { - return nil +func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { + // First we check that we are tracking this message, next we check that we haven't already sent this + var count uint64 + err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count) + if err != nil { + return false, err + } + + if count == 0 { + return false, nil + } + + err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? AND installation_id = ? `, messageID, crypto.CompressPubkey(publicKey), installationID).Scan(&count) + if err != nil { + return false, err + } + + return count == 0, nil +} + +func (p *Persistence) NotifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { + sentAt := time.Now().Unix() + _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, sent_at) VALUES (?, ?, ?, ?)`, crypto.CompressPubkey(publicKey), installationID, messageID, sentAt) + return err } func (p *Persistence) UpsertServer(server *PushNotificationServer) error { diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 7c4e55c73..f81699240 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -72,3 +72,69 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { s.Require().Equal(int64(2), retrievedServers[0].RegisteredAt) s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey)) } + +func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { + installationID1 := "installation-id-1" + installationID2 := "installation-id-2" + installationID3 := "installation-id-3" + key1, err := crypto.GenerateKey() + s.Require().NoError(err) + key2, err := crypto.GenerateKey() + s.Require().NoError(err) + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + + accessToken := "token" + + infos := []*PushNotificationInfo{ + { + PublicKey: &key1.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID1, + }, + { + PublicKey: &key1.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID2, + }, + { + PublicKey: &key1.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID3, + }, + { + PublicKey: &key2.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID1, + }, + { + PublicKey: &key2.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID2, + }, + { + PublicKey: &key2.PublicKey, + ServerPublicKey: &serverKey.PublicKey, + RetrievedAt: 1, + AccessToken: accessToken, + InstallationID: installationID3, + }, + } + + s.Require().NoError(s.persistence.SavePushNotificationInfo(infos)) + + retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key1.PublicKey, []string{installationID1, installationID2}) + s.Require().NoError(err) + + s.Require().Len(retrievedInfos, 2) +} diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/push_notification.go index 083a6d9d7..f5c8c229f 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/push_notification.go @@ -4,6 +4,7 @@ import ( "context" "crypto/aes" "crypto/cipher" + "sort" "bytes" "crypto/ecdsa" @@ -16,6 +17,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/google/uuid" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" @@ -24,6 +26,7 @@ import ( const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 +const staleQueryTimeInSeconds = 86400 type PushNotificationServer struct { PublicKey *ecdsa.PublicKey @@ -33,9 +36,11 @@ type PushNotificationServer struct { } type PushNotificationInfo struct { - AccessToken string - InstallationID string - PublicKey *ecdsa.PublicKey + AccessToken string + InstallationID string + PublicKey *ecdsa.PublicKey + ServerPublicKey *ecdsa.PublicKey + RetrievedAt int64 } type Config struct { @@ -79,18 +84,15 @@ type Client struct { //messageProcessor is a message processor used to send and being notified of messages messageProcessor *common.MessageProcessor - //pushNotificationQueryResponses is a channel that listens to pushNotificationResponse - pushNotificationQueryResponses chan *protobuf.PushNotificationQueryResponse } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { return &Client{ - quit: make(chan struct{}), - config: config, - pushNotificationQueryResponses: make(chan *protobuf.PushNotificationQueryResponse), - messageProcessor: processor, - persistence: persistence, - reader: rand.Reader} + quit: make(chan struct{}), + config: config, + messageProcessor: processor, + persistence: persistence, + reader: rand.Reader} } func (c *Client) Start() error { @@ -104,7 +106,7 @@ func (c *Client) Start() error { select { case m := <-subscription: if err := c.HandleMessageSent(m); err != nil { - // TODO: log + c.config.Logger.Error("failed to handle message", zap.Error(err)) } case <-c.quit: return @@ -119,23 +121,150 @@ func (c *Client) Stop() error { return nil } -// Sends an actual push notification, where do we get the chatID? -func sendPushNotificationTo(publicKey *ecdsa.PublicKey, chatID string) error { +type notificationSendingSpec struct { + serverPublicKey *ecdsa.PublicKey + installationID string + messageID []byte +} + +// The message has been sent +// We should: +// 1) Check whether we should notify on anything +// 2) Refresh info if necessaary +// 3) Sent push notifications +func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { + if !c.config.SendEnabled { + return nil + } + publicKey := sentMessage.PublicKey + var installationIDs []string + + var notificationSpecs []*notificationSendingSpec + + //Find if there's any actionable message + for _, messageID := range sentMessage.MessageIDs { + for _, installation := range sentMessage.Spec.Installations { + installationID := installation.ID + shouldNotify, err := c.shouldNotifyOn(publicKey, installationID, messageID) + if err != nil { + return err + } + if shouldNotify { + notificationSpecs = append(notificationSpecs, ¬ificationSendingSpec{ + installationID: installationID, + messageID: messageID, + }) + installationIDs = append(installationIDs, installation.ID) + } + } + } + + // Is there anything we should be notifying on? + if len(installationIDs) == 0 { + return nil + } + + // Check if we queried recently + queriedAt, err := c.persistence.GetQueriedAt(publicKey) + if err != nil { + return err + } + + // Naively query again if too much time has passed. + // Here it might not be necessary + if time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { + err := c.QueryPushNotificationInfo(publicKey) + if err != nil { + return err + } + // This is just horrible, but for now will do, + // the issue is that we don't really know how long it will + // take to reply, as there might be multiple servers + // replying to us. + // The only time we are 100% certain that we can proceed is + // when we have non-stale info for each device, but + // most devices are not going to be registered, so we'd still + // have to wait teh maximum amount of time allowed. + time.Sleep(3 * time.Second) + + } + // Retrieve infos + info, err := c.GetPushNotificationInfo(publicKey, installationIDs) + if err != nil { + return err + } + + // Naively dispatch to the first server for now + // This wait for an acknowledgement and try a different server after a timeout + // Also we sent a single notification for multiple message ids, need to check with UI what's the desired behavior + + // Sort by server so we tend to hit the same one + sort.Slice(info, func(i, j int) bool { + return info[i].ServerPublicKey.X.Cmp(info[j].ServerPublicKey.X) <= 0 + }) + + installationIDsMap := make(map[string]bool) + // One info per installation id, grouped by server + actionableInfos := make(map[string][]*PushNotificationInfo) + for _, i := range info { + if !installationIDsMap[i.InstallationID] { + serverKey := hex.EncodeToString(crypto.CompressPubkey(i.ServerPublicKey)) + actionableInfos[serverKey] = append(actionableInfos[serverKey], i) + installationIDsMap[i.InstallationID] = true + } + + } + + for _, infos := range actionableInfos { + var pushNotifications []*protobuf.PushNotification + for _, i := range infos { + // TODO: Add ChatID, message, public_key + pushNotifications = append(pushNotifications, &protobuf.PushNotification{ + AccessToken: i.AccessToken, + PublicKey: common.HashPublicKey(publicKey), + InstallationId: i.InstallationID, + }) + + } + request := &protobuf.PushNotificationRequest{ + MessageId: sentMessage.MessageIDs[0], + Requests: pushNotifications, + } + serverPublicKey := infos[0].ServerPublicKey + + payload, err := proto.Marshal(request) + if err != nil { + return err + } + + rawMessage := &common.RawMessage{ + Payload: payload, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, + } + + // TODO: We should use the messageID for the response + _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, rawMessage) + + if err != nil { + return err + } + } + return nil } -// This should schedule: -// 1) Check we have reasonably fresh push notifications info -// 2) Otherwise it should fetch them -// 3) Send a push notification to the devices in question -func (p *Client) HandleMessageSent(sentMessage *common.SentMessage) error { - return nil +// NotifyOnMessageID keeps track of the message to make sure we notify on it +func (c *Client) NotifyOnMessageID(chatID string, messageID []byte) error { + return c.persistence.TrackPushNotification(chatID, messageID) } -func (p *Client) NotifyOnMessageID(messageID []byte) error { - return nil +func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { + return c.persistence.ShouldSentNotificationFor(publicKey, installationID, messageID) } +func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { + return c.persistence.NotifiedOn(publicKey, installationID, messageID) +} func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte @@ -313,14 +442,42 @@ func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotifica } // HandlePushNotificationQueryResponse should update the data in the database for a given user -func (c *Client) HandlePushNotificationQueryResponse(response protobuf.PushNotificationQueryResponse) error { +func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { c.config.Logger.Debug("received push notification query response", zap.Any("response", response)) - select { - case c.pushNotificationQueryResponses <- &response: - default: - return errors.New("could not process push notification query response") + if len(response.Info) == 0 { + return errors.New("empty response from the server") } + + publicKey, err := c.persistence.GetQueryPublicKey(response.MessageId) + if err != nil { + return err + } + if publicKey == nil { + c.config.Logger.Debug("query not found") + return nil + } + var pushNotificationInfo []*PushNotificationInfo + for _, info := range response.Info { + if bytes.Compare(info.PublicKey, common.HashPublicKey(publicKey)) != 0 { + c.config.Logger.Warn("reply for different key, ignoring") + continue + } + pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ + PublicKey: publicKey, + ServerPublicKey: serverPublicKey, + AccessToken: info.AccessToken, + InstallationID: info.InstallationId, + RetrievedAt: time.Now().Unix(), + }) + + } + err = c.persistence.SavePushNotificationInfo(pushNotificationInfo) + if err != nil { + c.config.Logger.Error("failed to save push notifications", zap.Error(err)) + return err + } + return nil } @@ -347,14 +504,14 @@ func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { }) } -func (c *Client) RetrievePushNotificationInfo(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) { +func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { hashedPublicKey := common.HashPublicKey(publicKey) query := &protobuf.PushNotificationQuery{ PublicKeys: [][]byte{hashedPublicKey}, } encodedMessage, err := proto.Marshal(query) if err != nil { - return nil, err + return err } rawMessage := &common.RawMessage{ @@ -366,47 +523,20 @@ func (c *Client) RetrievePushNotificationInfo(publicKey *ecdsa.PublicKey) ([]*Pu c.config.Logger.Debug("sending query") messageID, err := c.messageProcessor.SendPublic(context.Background(), encodedPublicKey, rawMessage) - // TODO: this is probably best done by polling the database instead - for { - select { - case <-c.quit: - return nil, nil - case <-time.After(5 * time.Second): - return nil, errors.New("no registration query response received") - case response := <-c.pushNotificationQueryResponses: - if bytes.Compare(response.MessageId, messageID) != 0 { - // Not for us, queue back - c.pushNotificationQueryResponses <- response - // This is not accurate, we should then shrink the timeout - // Also we should handle multiple responses - continue - } - - if len(response.Info) == 0 { - return nil, errors.New("empty response from the server") - } - - var pushNotificationInfo []*PushNotificationInfo - for _, info := range response.Info { - if bytes.Compare(info.PublicKey, hashedPublicKey) != 0 { - continue - } - pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ - PublicKey: publicKey, - AccessToken: info.AccessToken, - InstallationID: info.InstallationId, - }) - - } - - return pushNotificationInfo, nil - } + if err != nil { + return err } + + return c.persistence.SavePushNotificationQuery(publicKey, messageID) } -func (s *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { +func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) { + return c.persistence.GetPushNotificationInfo(publicKey, installationIDs) +} + +func (c *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { encodedPublicKey := hex.EncodeToString(hashedPublicKey) - return s.messageProcessor.JoinPublic(encodedPublicKey) + return c.messageProcessor.JoinPublic(encodedPublicKey) } func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/push_notification_test.go index 7487a897d..852ea6ec1 100644 --- a/protocol/push_notification_client/push_notification_test.go +++ b/protocol/push_notification_client/push_notification_test.go @@ -3,7 +3,9 @@ package push_notification_client import ( "bytes" "crypto/ecdsa" + "io/ioutil" "math/rand" + "os" "testing" @@ -12,12 +14,52 @@ import ( "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" - "github.com/stretchr/testify/require" + "github.com/status-im/status-go/protocol/sqlite" + "github.com/status-im/status-go/protocol/tt" + "github.com/stretchr/testify/suite" ) -func TestBuildPushNotificationRegisterMessage(t *testing.T) { +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 + + database, err := sqlite.Open(s.tmpFile.Name(), "") + 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, + } + + s.client = New(s.persistence, config, nil) +} + +func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { myDeviceToken := "device-token" - myInstallationID := "installationID" mutedChatList := []string{"a", "b"} // build chat lish hashes @@ -26,9 +68,8 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) } - identity, err := crypto.GenerateKey() contactKey, err := crypto.GenerateKey() - require.NoError(t, err) + s.Require().NoError(err) contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} // Set random generator for uuid @@ -41,43 +82,77 @@ func TestBuildPushNotificationRegisterMessage(t *testing.T) { // set up reader reader := bytes.NewReader([]byte(expectedUUID)) - sharedKey, err := ecies.ImportECDSA(identity).GenerateShared( + sharedKey, err := ecies.ImportECDSA(s.identity).GenerateShared( ecies.ImportECDSAPublic(&contactKey.PublicKey), accessTokenKeyLength, accessTokenKeyLength, ) - require.NoError(t, err) + s.Require().NoError(err) // build encrypted token encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader) - require.NoError(t, err) + s.Require().NoError(err) // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - config := &Config{ - Identity: identity, - RemoteNotificationsEnabled: true, - InstallationID: myInstallationID, - } - - client := &Client{} - client.config = config - client.DeviceToken = myDeviceToken + s.client.DeviceToken = myDeviceToken // Set reader - client.reader = bytes.NewReader([]byte(expectedUUID)) + s.client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationRegistration{ Version: 1, AccessToken: expectedUUID, Token: myDeviceToken, - InstallationId: myInstallationID, + InstallationId: s.installationID, Enabled: true, BlockedChatList: mutedChatListHashes, AllowedUserList: [][]byte{encryptedToken}, } - actualMessage, err := client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) - require.NoError(t, err) + actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) + s.Require().NoError(err) - require.Equal(t, options, actualMessage) + s.Require().Equal(options, actualMessage) +} + +func (s *ClientSuite) TestNotifyOnMessageID() { + messageID := []byte("message-id") + chatID := "chat-id" + installationID1 := "1" + installationID2 := "2" + + s.Require().NoError(s.client.NotifyOnMessageID(chatID, messageID)) + + 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 + s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID)) + + // 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) } diff --git a/protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql b/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.down.sql similarity index 100% rename from protocol/push_notification_server/migrations/1593601728_initial_schema.down.sql rename to protocol/push_notification_server/migrations/sql/1593601728_initial_schema.down.sql diff --git a/protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql b/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql similarity index 100% rename from protocol/push_notification_server/migrations/1593601728_initial_schema.up.sql rename to protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql diff --git a/protocol/push_notification_server/migrations/doc.go b/protocol/push_notification_server/migrations/sql/doc.go similarity index 83% rename from protocol/push_notification_server/migrations/doc.go rename to protocol/push_notification_server/migrations/sql/doc.go index 0315ccce1..a7d080561 100644 --- a/protocol/push_notification_server/migrations/doc.go +++ b/protocol/push_notification_server/migrations/sql/doc.go @@ -6,4 +6,4 @@ package migrations -//go:generate go-bindata -pkg migrations -o ./migrations.go . +//go:generate go-bindata -pkg migrations -o ../migrations.go ./ diff --git a/protocol/push_notification_server/persistence.go b/protocol/push_notification_server/persistence.go index f7f73cb67..1d6eebe34 100644 --- a/protocol/push_notification_server/persistence.go +++ b/protocol/push_notification_server/persistence.go @@ -14,6 +14,8 @@ type Persistence interface { GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) // GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) + //GetPushNotificationRegistrationPublicKeys return all the public keys stored + GetPushNotificationRegistrationPublicKeys() ([][]byte, error) // DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id DeletePushNotificationRegistration(publicKey []byte, installationID string) error @@ -88,6 +90,26 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe return registrations, nil } +func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) { + rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations`) + if err != nil { + return nil, err + } + defer rows.Close() + + var publicKeys [][]byte + for rows.Next() { + var publicKey []byte + err := rows.Scan(&publicKey) + if err != nil { + return nil, err + } + + publicKeys = append(publicKeys, publicKey) + } + return publicKeys, nil +} + func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error { marshaledRegistration, err := proto.Marshal(registration) if err != nil { diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 223298859..04d1e56ba 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -163,6 +163,7 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio response.MessageId = request.MessageId + // TODO: filter by chat id // Collect successful requests & registrations var requestAndRegistrations []*RequestAndRegistration @@ -256,6 +257,20 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } +func (s *Server) Start() error { + pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys() + if err != nil { + return err + } + for _, pk := range pks { + if err := s.listenToPublicKeyQueryTopic(pk); err != nil { + return err + } + } + + return nil +} + func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { if s.messageProcessor == nil { return nil diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 310f81ffb..95961ff28 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -123,7 +123,8 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { bob1 := s.m bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) - client2 := s.newMessenger(s.shh) + alice := s.newMessenger(s.shh) + bobInstallationIDs := []string{bob1.installationID, bob2.installationID} // Register bob1 err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) @@ -213,11 +214,8 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().Len(bob2Servers, 1) s.Require().True(bob2Servers[0].Registered) - var info []*push_notification_client.PushNotificationInfo - go func() { - info, err = client2.pushNotificationClient.RetrievePushNotificationInfo(&bob2.identity.PublicKey) - errChan <- err - }() + err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) + s.Require().NoError(err) // Receive push notification query // TODO: find a better way to handle this waiting @@ -236,21 +234,23 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { // Receive push notification query response // TODO: find a better way to handle this waiting time.Sleep(500 * time.Millisecond) - _, err = client2.RetrieveAll() + _, err = alice.RetrieveAll() s.Require().NoError(err) time.Sleep(500 * time.Millisecond) - _, err = client2.RetrieveAll() + _, err = alice.RetrieveAll() s.Require().NoError(err) time.Sleep(500 * time.Millisecond) - _, err = client2.RetrieveAll() + _, err = alice.RetrieveAll() s.Require().NoError(err) - err = <-errChan + // Here we should poll, as we don't know whether they are already there + + info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) s.Require().NoError(err) - s.Require().NotNil(info) // Check we have replies for both bob1 and bob2 + s.Require().NotNil(info) s.Require().Len(info, 2) var bob1Info, bob2Info *push_notification_client.PushNotificationInfo @@ -264,17 +264,18 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { } s.Require().NotNil(bob1Info) - s.Require().Equal(bob1Info, &push_notification_client.PushNotificationInfo{ - InstallationID: bob1.installationID, - AccessToken: bob1Servers[0].AccessToken, - PublicKey: &bob1.identity.PublicKey, - }) + s.Require().Equal(bob1.installationID, bob1Info.InstallationID) + s.Require().Equal(bob1Info.AccessToken, bob1Servers[0].AccessToken, bob1Info.AccessToken) + s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) s.Require().NotNil(bob2Info) - s.Require().Equal(bob2Info, &push_notification_client.PushNotificationInfo{ - InstallationID: bob2.installationID, - AccessToken: bob2Servers[0].AccessToken, - PublicKey: &bob1.identity.PublicKey, - }) + s.Require().Equal(bob2.installationID, bob2Info.InstallationID) + s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken) + s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + alice.logger.Info("BOB KEY", zap.Any("key", bob1.identity.PublicKey)) + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 2) } From 83e859c75f0277dd1fa9c5e17fdab47d65e1e43c Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 10 Jul 2020 17:13:50 +0200 Subject: [PATCH 23/46] add migrations --- .../migrations/migrations.go | 319 ++++++++++++++++++ .../migrations/migrations.go | 319 ++++++++++++++++++ 2 files changed, 638 insertions(+) create mode 100644 protocol/push_notification_client/migrations/migrations.go create mode 100644 protocol/push_notification_server/migrations/migrations.go diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go new file mode 100644 index 000000000..7752d8ed8 --- /dev/null +++ b/protocol/push_notification_client/migrations/migrations.go @@ -0,0 +1,319 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// 1593601729_initial_schema.down.sql (144B) +// 1593601729_initial_schema.up.sql (1.284kB) +// doc.go (382B) + +package migrations + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var __1593601729_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x4f\xce\xc9\x4c\xcd\x2b\x89\x2f\x4e\x2d\x2a\x4b\x2d\x2a\xb6\xe6\x22\x46\x71\x66\x5e\x5a\x3e\x54\xa5\xa7\x9f\x8b\x6b\x84\x42\x66\x4a\x45\x3c\x5e\xd5\xf1\x05\xa5\x49\x39\x99\xc9\xf1\xd9\xa9\x95\xd6\x5c\x80\x00\x00\x00\xff\xff\x6d\xb4\xf8\x65\x90\x00\x00\x00") + +func _1593601729_initial_schemaDownSqlBytes() ([]byte, error) { + return bindataRead( + __1593601729_initial_schemaDownSql, + "1593601729_initial_schema.down.sql", + ) +} + +func _1593601729_initial_schemaDownSql() (*asset, error) { + bytes, err := _1593601729_initial_schemaDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa, 0x95, 0x55, 0x64, 0x38, 0x40, 0x16, 0xbf, 0x8b, 0x1c, 0x18, 0xb4, 0xc5, 0x7f, 0xd0, 0xb8, 0xf0, 0x3c, 0xa2, 0x82, 0xf8, 0x8d, 0x5a, 0xd3, 0xb6, 0x6e, 0xa3, 0xb4, 0xc, 0x9, 0x33, 0x0}} + return a, nil +} + +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x92\x4f\x8f\xaa\x30\x14\xc5\xf7\x7c\x8a\xbb\x94\x84\xc5\xdb\xbb\x02\x2c\x2f\x24\x4d\xfb\x9e\x96\xc4\x5d\xd3\x29\x57\x6d\x64\xc0\x69\xab\x19\xbf\xfd\xc4\xff\x22\x0e\x4e\x1c\x37\x24\xb4\xa7\xf7\xde\xdf\x3d\x27\x1d\x93\x58\x10\x10\x71\x42\x09\xe4\x19\x30\x2e\x80\x4c\xf3\x89\x98\xc0\x6a\xed\x16\xb2\x6e\xbc\x99\x19\xad\xbc\x69\x6a\xa9\x2b\x83\xb5\x97\x0e\xed\x06\xad\x83\x41\x00\xb0\x5a\xbf\x55\x46\xcb\x25\x6e\x21\xa1\x3c\xd9\xbf\x67\x05\xa5\x51\x00\x60\x71\x6e\x9c\x47\x8b\x25\x24\x9c\x53\x12\x33\x18\x91\x2c\x2e\xa8\x80\x2c\xa6\x13\xd2\xd6\x48\xe5\x21\x67\xe2\x5c\xe1\xac\xfd\xb3\xd3\x29\xad\xd1\x39\xe9\x9b\x25\xd6\x20\xc8\x54\xec\x0e\x0b\x96\xff\x2f\xc8\xe0\x32\x43\x08\x9c\x41\xca\x59\x46\xf3\x54\xc0\x98\xfc\xa3\x71\x4a\x82\x70\x18\x04\xcf\x70\x7e\xac\xd1\x1a\x7c\xcc\x79\xd0\x75\x00\x4e\x57\x5b\x69\xca\xee\xa3\xce\xec\xd1\x49\xfb\x5a\x08\x53\xcf\x9a\x87\x04\x07\x47\x65\x9f\xc4\xd4\xce\xab\xaa\x3a\xd4\x36\xe5\xde\x83\x96\xa0\xe3\xd0\x4d\x16\xbc\x35\xb8\xb9\xbf\xa5\xee\x2e\x6e\xdb\x45\xdd\x11\x5f\xbb\x26\x6f\x95\x5e\x62\x29\xdf\xd1\x39\x35\x3f\x9a\x7e\xfc\xb9\xeb\x9f\x5e\x28\x7f\x77\x0f\xa7\x4a\xdf\x73\x5e\xca\xb6\x19\xf2\xbf\x8c\x8f\x49\x00\xf0\x2c\x84\xdb\x7d\xae\x2f\x1e\x63\xfc\xca\xf2\x7d\xbf\x9f\x70\x46\xd0\xe3\x6d\xd8\x26\xce\xd9\x88\x4c\xc1\x94\x9f\xb2\x37\xd3\xd7\x61\xe5\xac\x3f\xff\x7d\xc9\x0a\x87\xc1\x57\x00\x00\x00\xff\xff\xca\x86\xd6\x11\x04\x05\x00\x00") + +func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { + return bindataRead( + __1593601729_initial_schemaUpSql, + "1593601729_initial_schema.up.sql", + ) +} + +func _1593601729_initial_schemaUpSql() (*asset, error) { + bytes, err := _1593601729_initial_schemaUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1284, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6e, 0xee, 0xca, 0x95, 0x4b, 0xf0, 0x61, 0x6, 0xc, 0xad, 0x52, 0xe1, 0x81, 0x1a, 0x2d, 0xff, 0x4f, 0xb4, 0x2f, 0x6b, 0x56, 0xdf, 0x5c, 0xa7, 0xe8, 0x56, 0xaa, 0xe5, 0x6e, 0x7f, 0xb8}} + return a, nil +} + +var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00") + +func docGoBytes() ([]byte, error) { + return bindataRead( + _docGo, + "doc.go", + ) +} + +func docGo() (*asset, error) { + bytes, err := docGoBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "1593601729_initial_schema.down.sql": _1593601729_initial_schemaDownSql, + + "1593601729_initial_schema.up.sql": _1593601729_initial_schemaUpSql, + + "doc.go": docGo, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, map[string]*bintree{}}, + "1593601729_initial_schema.up.sql": &bintree{_1593601729_initial_schemaUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} diff --git a/protocol/push_notification_server/migrations/migrations.go b/protocol/push_notification_server/migrations/migrations.go new file mode 100644 index 000000000..07992c252 --- /dev/null +++ b/protocol/push_notification_server/migrations/migrations.go @@ -0,0 +1,319 @@ +// Code generated by go-bindata. DO NOT EDIT. +// sources: +// 1593601728_initial_schema.down.sql (200B) +// 1593601728_initial_schema.up.sql (517B) +// doc.go (382B) + +package migrations + +import ( + "bytes" + "compress/gzip" + "crypto/sha256" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" +) + +func bindataRead(data []byte, name string) ([]byte, error) { + gz, err := gzip.NewReader(bytes.NewBuffer(data)) + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + + var buf bytes.Buffer + _, err = io.Copy(&buf, gz) + clErr := gz.Close() + + if err != nil { + return nil, fmt.Errorf("read %q: %v", name, err) + } + if clErr != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +type asset struct { + bytes []byte + info os.FileInfo + digest [sha256.Size]byte +} + +type bindataFileInfo struct { + name string + size int64 + mode os.FileMode + modTime time.Time +} + +func (fi bindataFileInfo) Name() string { + return fi.name +} +func (fi bindataFileInfo) Size() int64 { + return fi.size +} +func (fi bindataFileInfo) Mode() os.FileMode { + return fi.mode +} +func (fi bindataFileInfo) ModTime() time.Time { + return fi.modTime +} +func (fi bindataFileInfo) IsDir() bool { + return false +} +func (fi bindataFileInfo) Sys() interface{} { + return nil +} + +var __1593601728_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x8a\x2f\x4a\x4d\xcf\x2c\x2e\x29\x02\x8b\x15\x5b\x73\x81\xb5\x78\xfa\xb9\xb8\x46\x28\x64\xa6\x54\xc4\x13\xa7\x2d\xbe\xa0\x34\x29\x27\x33\x39\x3e\x3b\xb5\x92\x72\x13\xe2\x33\xf3\x8a\x4b\x12\x73\x72\x20\x8a\x33\x53\xac\xb9\xb8\x00\x01\x00\x00\xff\xff\x90\x39\xe0\x1c\xc8\x00\x00\x00") + +func _1593601728_initial_schemaDownSqlBytes() ([]byte, error) { + return bindataRead( + __1593601728_initial_schemaDownSql, + "1593601728_initial_schema.down.sql", + ) +} + +func _1593601728_initial_schemaDownSql() (*asset, error) { + bytes, err := _1593601728_initial_schemaDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}} + return a, nil +} + +var __1593601728_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x90\xc1\x4a\x03\x31\x14\x45\xf7\xf9\x8a\xbb\xec\x40\xff\x60\x56\x99\x98\x62\x20\xbc\xe8\x34\x23\xdd\x85\xda\x46\x7d\x38\xcc\x94\x24\x2d\xfa\xf7\xe2\x28\x38\xad\x9b\x22\x6e\x2f\x8f\x7b\xde\x3d\xaa\xd5\xd2\x6b\x78\xd9\x58\x0d\xb3\x02\x39\x0f\xbd\x31\x6b\xbf\xc6\xe1\x98\x5f\xc2\x30\x16\x7e\xe2\xdd\xb6\xf0\x38\x84\x1c\xd3\x29\xa6\x90\xe2\x33\xe7\x92\xa6\x2c\x63\x21\x80\xc3\xf1\xb1\xe7\x5d\x78\x8d\xef\x68\xac\x6b\xa6\x16\xea\xac\x5d\x0a\x80\x87\x5c\xb6\x7d\xff\xd5\xc0\x7b\x3c\xc8\x56\xdd\xca\xf6\xec\xe6\x14\x53\xe6\x71\x80\x21\x7f\x96\xcf\x49\x53\xf3\x67\xd8\x91\xb9\xef\xf4\xe2\x87\xb9\xbc\x64\x54\x70\x04\xe5\x68\x65\x8d\xf2\x68\xf5\x9d\x95\x4a\x8b\xaa\x16\xe2\x7b\xae\xa1\x1b\xbd\x01\xef\xdf\xc2\x75\x23\xc3\x6c\xa0\xa3\x2b\xcd\xcc\x3e\xac\xea\x7f\x20\x87\x4b\x93\x7f\xf9\xe4\xb7\xab\x5a\x88\x8f\x00\x00\x00\xff\xff\x5c\xf8\x30\xd8\x05\x02\x00\x00") + +func _1593601728_initial_schemaUpSqlBytes() ([]byte, error) { + return bindataRead( + __1593601728_initial_schemaUpSql, + "1593601728_initial_schema.up.sql", + ) +} + +func _1593601728_initial_schemaUpSql() (*asset, error) { + bytes, err := _1593601728_initial_schemaUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 517, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf1, 0x55, 0xab, 0xbf, 0xee, 0x66, 0xe9, 0x81, 0x9f, 0x89, 0xca, 0x6b, 0xf8, 0x1d, 0x12, 0x59, 0xf6, 0x1e, 0xac, 0x79, 0x93, 0x8f, 0x47, 0xbd, 0x76, 0x65, 0xbb, 0x10, 0x99, 0x8a, 0xda, 0x9e}} + return a, nil +} + +var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00") + +func docGoBytes() ([]byte, error) { + return bindataRead( + _docGo, + "doc.go", + ) +} + +func docGo() (*asset, error) { + bytes, err := docGoBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} + return a, nil +} + +// Asset loads and returns the asset for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func Asset(name string) ([]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err) + } + return a.bytes, nil + } + return nil, fmt.Errorf("Asset %s not found", name) +} + +// AssetString returns the asset contents as a string (instead of a []byte). +func AssetString(name string) (string, error) { + data, err := Asset(name) + return string(data), err +} + +// MustAsset is like Asset but panics when Asset would return an error. +// It simplifies safe initialization of global variables. +func MustAsset(name string) []byte { + a, err := Asset(name) + if err != nil { + panic("asset: Asset(" + name + "): " + err.Error()) + } + + return a +} + +// MustAssetString is like AssetString but panics when Asset would return an +// error. It simplifies safe initialization of global variables. +func MustAssetString(name string) string { + return string(MustAsset(name)) +} + +// AssetInfo loads and returns the asset info for the given name. +// It returns an error if the asset could not be found or +// could not be loaded. +func AssetInfo(name string) (os.FileInfo, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err) + } + return a.info, nil + } + return nil, fmt.Errorf("AssetInfo %s not found", name) +} + +// AssetDigest returns the digest of the file with the given name. It returns an +// error if the asset could not be found or the digest could not be loaded. +func AssetDigest(name string) ([sha256.Size]byte, error) { + canonicalName := strings.Replace(name, "\\", "/", -1) + if f, ok := _bindata[canonicalName]; ok { + a, err := f() + if err != nil { + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err) + } + return a.digest, nil + } + return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name) +} + +// Digests returns a map of all known files and their checksums. +func Digests() (map[string][sha256.Size]byte, error) { + mp := make(map[string][sha256.Size]byte, len(_bindata)) + for name := range _bindata { + a, err := _bindata[name]() + if err != nil { + return nil, err + } + mp[name] = a.digest + } + return mp, nil +} + +// AssetNames returns the names of the assets. +func AssetNames() []string { + names := make([]string, 0, len(_bindata)) + for name := range _bindata { + names = append(names, name) + } + return names +} + +// _bindata is a table, holding each asset generator, mapped to its name. +var _bindata = map[string]func() (*asset, error){ + "1593601728_initial_schema.down.sql": _1593601728_initial_schemaDownSql, + + "1593601728_initial_schema.up.sql": _1593601728_initial_schemaUpSql, + + "doc.go": docGo, +} + +// AssetDir returns the file names below a certain +// directory embedded in the file by go-bindata. +// For example if you run go-bindata on data/... and data contains the +// following hierarchy: +// data/ +// foo.txt +// img/ +// a.png +// b.png +// then AssetDir("data") would return []string{"foo.txt", "img"}, +// AssetDir("data/img") would return []string{"a.png", "b.png"}, +// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and +// AssetDir("") will return []string{"data"}. +func AssetDir(name string) ([]string, error) { + node := _bintree + if len(name) != 0 { + canonicalName := strings.Replace(name, "\\", "/", -1) + pathList := strings.Split(canonicalName, "/") + for _, p := range pathList { + node = node.Children[p] + if node == nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + } + } + if node.Func != nil { + return nil, fmt.Errorf("Asset %s not found", name) + } + rv := make([]string, 0, len(node.Children)) + for childName := range node.Children { + rv = append(rv, childName) + } + return rv, nil +} + +type bintree struct { + Func func() (*asset, error) + Children map[string]*bintree +} + +var _bintree = &bintree{nil, map[string]*bintree{ + "1593601728_initial_schema.down.sql": &bintree{_1593601728_initial_schemaDownSql, map[string]*bintree{}}, + "1593601728_initial_schema.up.sql": &bintree{_1593601728_initial_schemaUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, +}} + +// RestoreAsset restores an asset under the given directory. +func RestoreAsset(dir, name string) error { + data, err := Asset(name) + if err != nil { + return err + } + info, err := AssetInfo(name) + if err != nil { + return err + } + err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) + if err != nil { + return err + } + err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) + if err != nil { + return err + } + return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) +} + +// RestoreAssets restores an asset under the given directory recursively. +func RestoreAssets(dir, name string) error { + children, err := AssetDir(name) + // File + if err != nil { + return RestoreAsset(dir, name) + } + // Dir + for _, child := range children { + err = RestoreAssets(dir, filepath.Join(name, child)) + if err != nil { + return err + } + } + return nil +} + +func _filePath(dir, name string) string { + canonicalName := strings.Replace(name, "\\", "/", -1) + return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...) +} From 3f4575b80264018d897355c69da1f2a68c9995de Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 13 Jul 2020 10:53:13 +0200 Subject: [PATCH 24/46] Add grant validation --- PUSH-NOTIFICATIONS-TODO.txt | 1 + protocol/protobuf/push_notifications.pb.go | 125 ++++++++++-------- protocol/protobuf/push_notifications.proto | 2 + .../{push_notification.go => client.go} | 61 ++++++++- ...sh_notification_test.go => client_test.go} | 0 protocol/push_notification_server/errors.go | 1 + protocol/push_notification_server/server.go | 52 +++++++- .../push_notification_server/server_test.go | 58 ++++++++ 8 files changed, 236 insertions(+), 64 deletions(-) rename protocol/push_notification_client/{push_notification.go => client.go} (87%) rename protocol/push_notification_client/{push_notification_test.go => client_test.go} (100%) diff --git a/PUSH-NOTIFICATIONS-TODO.txt b/PUSH-NOTIFICATIONS-TODO.txt index 2e0c858b9..2716e3e99 100644 --- a/PUSH-NOTIFICATIONS-TODO.txt +++ b/PUSH-NOTIFICATIONS-TODO.txt @@ -2,3 +2,4 @@ Specs changes: - Use application metadata wrapper - Encrypt of payload instead of signature + public key of server - Removed preferencs + each device registers individually +- Add grant diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index c59a9016a..d3ca7b1d7 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -123,6 +123,7 @@ type PushNotificationRegistration struct { AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` + Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -216,6 +217,13 @@ func (m *PushNotificationRegistration) GetUnregister() bool { return false } +func (m *PushNotificationRegistration) GetGrant() []byte { + if m != nil { + return m.Grant + } + return nil +} + type PushNotificationRegistrationResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` @@ -409,6 +417,7 @@ type PushNotificationQueryInfo struct { InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` AllowedUserList [][]byte `protobuf:"bytes,4,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + Grant []byte `protobuf:"bytes,5,opt,name=grant,proto3" json:"grant,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -467,6 +476,13 @@ func (m *PushNotificationQueryInfo) GetAllowedUserList() [][]byte { return nil } +func (m *PushNotificationQueryInfo) GetGrant() []byte { + if m != nil { + return m.Grant + } + return nil +} + type PushNotificationQueryResponse struct { Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` @@ -770,58 +786,59 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 836 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x51, 0x6f, 0xe3, 0x44, - 0x10, 0xc7, 0xb1, 0x9d, 0x36, 0xf1, 0x34, 0xb4, 0xbe, 0x55, 0xaf, 0x67, 0x4e, 0x1c, 0xe4, 0x0c, - 0x12, 0xd1, 0x21, 0x45, 0xa8, 0x48, 0x70, 0xe2, 0x89, 0xd0, 0xba, 0xc5, 0x6a, 0x63, 0x87, 0x8d, - 0xc3, 0x09, 0x09, 0xc9, 0x72, 0xe2, 0xcd, 0xc5, 0xaa, 0xcf, 0x36, 0xde, 0xf5, 0xa1, 0x3c, 0x20, - 0xf1, 0x09, 0x90, 0x78, 0xe5, 0x53, 0xa0, 0xfb, 0x0e, 0x7c, 0x2f, 0xe4, 0xb5, 0x1d, 0x1c, 0xc7, - 0x4d, 0xf3, 0xc0, 0x93, 0x3d, 0xb3, 0x33, 0xbb, 0x3b, 0xbf, 0xd9, 0xf9, 0x83, 0x1a, 0xa7, 0x74, - 0xe9, 0x84, 0x11, 0xf3, 0x17, 0xfe, 0xdc, 0x65, 0x7e, 0x14, 0xd2, 0x41, 0x9c, 0x44, 0x2c, 0x42, - 0x1d, 0xfe, 0x99, 0xa5, 0x0b, 0xed, 0x1f, 0x09, 0x3e, 0x1c, 0xa7, 0x74, 0x69, 0x56, 0xa2, 0x30, - 0x79, 0xed, 0x53, 0x96, 0xf0, 0x7f, 0x64, 0x01, 0xb0, 0xe8, 0x8e, 0x84, 0x0e, 0x5b, 0xc5, 0x44, - 0x15, 0x7a, 0x42, 0xff, 0xf8, 0xfc, 0x8b, 0x41, 0x99, 0x3f, 0xd8, 0x95, 0x3b, 0xb0, 0xb3, 0x44, - 0x7b, 0x15, 0x13, 0x2c, 0xb3, 0xf2, 0x17, 0x9d, 0xc2, 0x01, 0x37, 0x54, 0xb1, 0x27, 0xf4, 0x65, - 0x9c, 0x1b, 0xe8, 0x33, 0x38, 0xf1, 0x43, 0xca, 0xdc, 0x20, 0xe0, 0xa9, 0x8e, 0xef, 0xa9, 0x12, - 0x5f, 0x3f, 0xae, 0xba, 0x0d, 0x0f, 0x3d, 0x87, 0xae, 0x3b, 0x9f, 0x13, 0x4a, 0x9d, 0x7c, 0x97, - 0x16, 0x8f, 0x3a, 0xca, 0x7d, 0xfc, 0x40, 0xa4, 0x42, 0x9b, 0x84, 0xee, 0x2c, 0x20, 0x9e, 0x7a, - 0xd0, 0x13, 0xfa, 0x1d, 0x5c, 0x9a, 0xd9, 0xca, 0x5b, 0x92, 0x50, 0x3f, 0x0a, 0xd5, 0xc3, 0x9e, - 0xd0, 0x6f, 0xe1, 0xd2, 0x44, 0x2f, 0xe0, 0x91, 0x1b, 0x04, 0xd1, 0xaf, 0xc4, 0x73, 0x52, 0x4a, - 0x12, 0x27, 0xf0, 0x29, 0x53, 0xdb, 0x3d, 0xa9, 0xdf, 0xc5, 0x27, 0xc5, 0xc2, 0x94, 0x92, 0xe4, - 0xd6, 0xa7, 0x2c, 0x8b, 0x9d, 0x05, 0xd1, 0xfc, 0x8e, 0x78, 0xce, 0x7c, 0xe9, 0xb2, 0x3c, 0xb6, - 0x93, 0xc7, 0x16, 0x0b, 0x17, 0x4b, 0x97, 0xf1, 0xd8, 0x8f, 0x00, 0xd2, 0x30, 0xe1, 0x50, 0x48, - 0xa2, 0xca, 0xfc, 0x3a, 0x15, 0x8f, 0x76, 0x05, 0xf2, 0x9a, 0x12, 0x3a, 0x03, 0x34, 0x35, 0x6f, - 0x4c, 0xeb, 0x95, 0xe9, 0xd8, 0xd6, 0x8d, 0x6e, 0x3a, 0xf6, 0x4f, 0x63, 0x5d, 0x79, 0x0f, 0xbd, - 0x0f, 0xf2, 0x70, 0x5c, 0xf8, 0x14, 0x01, 0x21, 0x38, 0xbe, 0x32, 0xb0, 0xfe, 0xdd, 0x70, 0xa2, - 0x17, 0x3e, 0x51, 0x7b, 0x27, 0xc2, 0xa7, 0xbb, 0x7a, 0x81, 0x09, 0x8d, 0xa3, 0x90, 0x92, 0x0c, - 0x01, 0x4d, 0x39, 0x2c, 0xde, 0xcc, 0x0e, 0x2e, 0x4d, 0x64, 0xc2, 0x01, 0x49, 0x92, 0x28, 0xe1, - 0x8d, 0x39, 0x3e, 0x7f, 0xb9, 0x5f, 0x93, 0xcb, 0x8d, 0x07, 0x7a, 0x96, 0xcb, 0x9b, 0x9d, 0x6f, - 0x83, 0x9e, 0x01, 0x24, 0xe4, 0x97, 0x94, 0x50, 0x56, 0x76, 0xb3, 0x8b, 0xe5, 0xc2, 0x63, 0x78, - 0xda, 0xef, 0x02, 0xc8, 0xeb, 0x9c, 0x6a, 0xe9, 0x3a, 0xc6, 0x16, 0x2e, 0x4b, 0x7f, 0x0c, 0x8f, - 0x46, 0xc3, 0xdb, 0x2b, 0x0b, 0x8f, 0xf4, 0x4b, 0x67, 0xa4, 0x4f, 0x26, 0xc3, 0x6b, 0x5d, 0x11, - 0xd0, 0x29, 0x28, 0x3f, 0xea, 0x78, 0x62, 0x58, 0xa6, 0x33, 0x32, 0x26, 0xa3, 0xa1, 0x7d, 0xf1, - 0xbd, 0x22, 0xa2, 0xa7, 0x70, 0x36, 0x35, 0x27, 0xd3, 0xf1, 0xd8, 0xc2, 0xb6, 0x7e, 0x59, 0x65, - 0x28, 0x65, 0xd0, 0x0c, 0xd3, 0xd6, 0xb1, 0x39, 0xbc, 0xcd, 0x4f, 0x50, 0x5a, 0xda, 0x1f, 0x02, - 0x3c, 0xaf, 0xd7, 0x36, 0xf4, 0xde, 0x92, 0x84, 0xf9, 0x94, 0xbc, 0x21, 0x21, 0x33, 0xc2, 0x45, - 0x94, 0xd5, 0x11, 0xa7, 0xb3, 0xc0, 0x9f, 0x3b, 0x77, 0x64, 0xc5, 0xa1, 0x75, 0xb1, 0x9c, 0x7b, - 0x6e, 0xc8, 0x6a, 0xeb, 0x41, 0x8a, 0xdb, 0x0f, 0x72, 0xdf, 0xc7, 0xad, 0xfd, 0x06, 0xea, 0x45, - 0x14, 0x32, 0x77, 0xce, 0x2e, 0x22, 0x8f, 0x6c, 0x5c, 0x05, 0xb9, 0x70, 0xb6, 0x35, 0xcf, 0x8e, - 0x1f, 0x2e, 0x22, 0x55, 0xe8, 0x49, 0xfd, 0xa3, 0xf3, 0xcf, 0xef, 0xef, 0xd7, 0x56, 0x4d, 0xf8, - 0x34, 0xae, 0x85, 0x64, 0x5e, 0xed, 0x25, 0x3c, 0xae, 0xa7, 0xfe, 0x90, 0x92, 0x64, 0x85, 0x3e, - 0x86, 0xa3, 0xff, 0x10, 0x50, 0x7e, 0x60, 0x17, 0xc3, 0x9a, 0x01, 0xd5, 0xde, 0x09, 0xf0, 0x41, - 0x63, 0x2a, 0x27, 0x58, 0x47, 0x24, 0xec, 0x85, 0x48, 0x6c, 0x9c, 0xff, 0xcd, 0x6e, 0x48, 0xf5, - 0x6e, 0x34, 0xce, 0x71, 0xab, 0x71, 0x8e, 0xb5, 0x3f, 0x05, 0x78, 0xd6, 0x78, 0xe9, 0xf5, 0xb0, - 0x7c, 0x0d, 0xad, 0x0a, 0xe1, 0x4f, 0xee, 0x27, 0xbc, 0xae, 0x15, 0xf3, 0x84, 0xec, 0x96, 0x6f, - 0x08, 0xa5, 0xee, 0x6b, 0x52, 0x56, 0xd2, 0xc5, 0x72, 0xe1, 0x31, 0xbc, 0xea, 0x10, 0x4a, 0x1b, - 0x43, 0xa8, 0xfd, 0x2d, 0x80, 0x52, 0xdf, 0x7c, 0x1f, 0x7e, 0x4f, 0xa0, 0xcd, 0xb5, 0x68, 0xcd, - 0xed, 0x30, 0x33, 0x1f, 0xe6, 0xd5, 0xc0, 0xbd, 0xd5, 0xc8, 0x5d, 0x85, 0x76, 0x71, 0x7f, 0x2e, - 0xaa, 0x5d, 0x5c, 0x9a, 0x5a, 0x0c, 0x4f, 0xb6, 0x05, 0x82, 0x4f, 0x39, 0xfa, 0x0a, 0x3a, 0xc5, - 0xc0, 0xd3, 0x82, 0xe1, 0xd3, 0x1d, 0xaa, 0xb2, 0x8e, 0x7d, 0x00, 0x9f, 0xf6, 0x97, 0x08, 0x67, - 0xdb, 0x47, 0xc6, 0x51, 0xc2, 0x76, 0xc8, 0xdb, 0xb7, 0x9b, 0xf2, 0xf6, 0x62, 0x97, 0xbc, 0x65, - 0x5b, 0x35, 0x0a, 0xda, 0xff, 0x81, 0x52, 0xfb, 0x79, 0x1f, 0xe1, 0x3b, 0x81, 0xa3, 0x57, 0xd8, - 0x32, 0xaf, 0xab, 0xaa, 0x5f, 0x13, 0x30, 0x31, 0xf3, 0x99, 0x96, 0xed, 0x60, 0xfd, 0xda, 0x98, - 0xd8, 0x3a, 0xd6, 0x2f, 0x15, 0x49, 0x4b, 0x41, 0xdd, 0x2e, 0xa8, 0x78, 0xcf, 0x9b, 0x5c, 0x85, - 0xfa, 0xb3, 0xfc, 0x06, 0xda, 0x09, 0xaf, 0x9d, 0xaa, 0x22, 0xef, 0x56, 0xef, 0x21, 0x48, 0xb8, - 0x4c, 0x98, 0x1d, 0xf2, 0xc8, 0x2f, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x28, 0xe3, 0xa0, - 0x75, 0x08, 0x00, 0x00, + // 856 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xe1, 0x6e, 0xe3, 0x44, + 0x10, 0xc7, 0x59, 0x27, 0x6d, 0xe2, 0x69, 0x68, 0x73, 0xab, 0x5e, 0xcf, 0x9c, 0x38, 0xc8, 0x19, + 0x24, 0xa2, 0x43, 0x8a, 0x50, 0x91, 0xe0, 0xc4, 0x27, 0x42, 0xeb, 0x16, 0xab, 0x8d, 0x1d, 0x36, + 0x2e, 0x27, 0x24, 0x24, 0xcb, 0x89, 0xb7, 0x8d, 0xd5, 0x9c, 0x6d, 0x76, 0xd7, 0x87, 0xf2, 0x01, + 0x89, 0x27, 0x40, 0xe2, 0x2b, 0x4f, 0x81, 0x78, 0x0a, 0xde, 0x81, 0x97, 0x41, 0x5e, 0xdb, 0xc1, + 0x71, 0x7c, 0x69, 0x3e, 0xf0, 0x29, 0x9e, 0xd9, 0x99, 0xdd, 0x9d, 0xdf, 0xec, 0xfc, 0x03, 0x5a, + 0x9c, 0xf0, 0xb9, 0x1b, 0x46, 0x22, 0xb8, 0x0d, 0x66, 0x9e, 0x08, 0xa2, 0x90, 0x0f, 0x62, 0x16, + 0x89, 0x08, 0xb7, 0xe5, 0xcf, 0x34, 0xb9, 0xd5, 0xff, 0x69, 0xc0, 0xfb, 0xe3, 0x84, 0xcf, 0xad, + 0x52, 0x14, 0xa1, 0x77, 0x01, 0x17, 0x4c, 0x7e, 0x63, 0x1b, 0x40, 0x44, 0xf7, 0x34, 0x74, 0xc5, + 0x32, 0xa6, 0x1a, 0xea, 0xa1, 0xfe, 0xe1, 0xe9, 0x67, 0x83, 0x22, 0x7f, 0xb0, 0x2d, 0x77, 0xe0, + 0xa4, 0x89, 0xce, 0x32, 0xa6, 0x44, 0x15, 0xc5, 0x27, 0x3e, 0x86, 0x3d, 0x69, 0x68, 0x4a, 0x0f, + 0xf5, 0x55, 0x92, 0x19, 0xf8, 0x13, 0x38, 0x0a, 0x42, 0x2e, 0xbc, 0xc5, 0x42, 0xa6, 0xba, 0x81, + 0xaf, 0x35, 0xe4, 0xfa, 0x61, 0xd9, 0x6d, 0xfa, 0xf8, 0x39, 0x74, 0xbc, 0xd9, 0x8c, 0x72, 0xee, + 0x66, 0xbb, 0x34, 0x65, 0xd4, 0x41, 0xe6, 0x93, 0x07, 0x62, 0x0d, 0x5a, 0x34, 0xf4, 0xa6, 0x0b, + 0xea, 0x6b, 0x7b, 0x3d, 0xd4, 0x6f, 0x93, 0xc2, 0x4c, 0x57, 0xde, 0x50, 0xc6, 0x83, 0x28, 0xd4, + 0xf6, 0x7b, 0xa8, 0xdf, 0x24, 0x85, 0x89, 0x5f, 0xc0, 0x23, 0x6f, 0xb1, 0x88, 0x7e, 0xa6, 0xbe, + 0x9b, 0x70, 0xca, 0xdc, 0x45, 0xc0, 0x85, 0xd6, 0xea, 0x35, 0xfa, 0x1d, 0x72, 0x94, 0x2f, 0xdc, + 0x70, 0xca, 0xae, 0x03, 0x2e, 0xd2, 0xd8, 0xe9, 0x22, 0x9a, 0xdd, 0x53, 0xdf, 0x9d, 0xcd, 0x3d, + 0x91, 0xc5, 0xb6, 0xb3, 0xd8, 0x7c, 0xe1, 0x6c, 0xee, 0x09, 0x19, 0xfb, 0x01, 0x40, 0x12, 0x32, + 0x09, 0x85, 0x32, 0x4d, 0x95, 0xd7, 0x29, 0x79, 0x52, 0x1a, 0x77, 0xcc, 0x0b, 0x85, 0x06, 0x3d, + 0xd4, 0xef, 0x90, 0xcc, 0xd0, 0x2f, 0x40, 0x5d, 0xb1, 0xc3, 0x27, 0x80, 0x6f, 0xac, 0x2b, 0xcb, + 0x7e, 0x65, 0xb9, 0x8e, 0x7d, 0x65, 0x58, 0xae, 0xf3, 0xc3, 0xd8, 0xe8, 0xbe, 0x83, 0xdf, 0x05, + 0x75, 0x38, 0xce, 0x7d, 0x5d, 0x84, 0x31, 0x1c, 0x5e, 0x98, 0xc4, 0xf8, 0x66, 0x38, 0x31, 0x72, + 0x9f, 0xa2, 0xff, 0xa5, 0xc0, 0xc7, 0xdb, 0x3a, 0x44, 0x28, 0x8f, 0xa3, 0x90, 0xd3, 0x14, 0x0c, + 0x4f, 0x24, 0x42, 0xd9, 0xe2, 0x36, 0x29, 0x4c, 0x6c, 0xc1, 0x1e, 0x65, 0x2c, 0x62, 0xb2, 0x5d, + 0x87, 0xa7, 0x2f, 0x77, 0x6b, 0x7d, 0xb1, 0xf1, 0xc0, 0x48, 0x73, 0xe5, 0x13, 0xc8, 0xb6, 0xc1, + 0xcf, 0x00, 0x18, 0xfd, 0x29, 0xa1, 0x5c, 0x14, 0x3d, 0xee, 0x10, 0x35, 0xf7, 0x98, 0xbe, 0xfe, + 0x2b, 0x02, 0x75, 0x95, 0x53, 0x2e, 0xdd, 0x20, 0xc4, 0x26, 0x45, 0xe9, 0x8f, 0xe1, 0xd1, 0x68, + 0x78, 0x7d, 0x61, 0x93, 0x91, 0x71, 0xee, 0x8e, 0x8c, 0xc9, 0x64, 0x78, 0x69, 0x74, 0x11, 0x3e, + 0x86, 0xee, 0xf7, 0x06, 0x99, 0x98, 0xb6, 0xe5, 0x8e, 0xcc, 0xc9, 0x68, 0xe8, 0x9c, 0x7d, 0xdb, + 0x55, 0xf0, 0x53, 0x38, 0xb9, 0xb1, 0x26, 0x37, 0xe3, 0xb1, 0x4d, 0x1c, 0xe3, 0xbc, 0xcc, 0xb0, + 0x91, 0x42, 0x33, 0x2d, 0xc7, 0x20, 0xd6, 0xf0, 0x3a, 0x3b, 0xa1, 0xdb, 0xd4, 0x7f, 0x43, 0xf0, + 0xbc, 0x5a, 0xdb, 0xd0, 0x7f, 0x43, 0x99, 0x08, 0x38, 0x7d, 0x4d, 0x43, 0x61, 0x86, 0xb7, 0x51, + 0x5a, 0x47, 0x9c, 0x4c, 0x17, 0xc1, 0xcc, 0xbd, 0xa7, 0x4b, 0x09, 0xad, 0x43, 0xd4, 0xcc, 0x73, + 0x45, 0x97, 0x1b, 0xcf, 0x54, 0xd9, 0x7c, 0xa6, 0xbb, 0x3e, 0x79, 0xfd, 0x17, 0xd0, 0xce, 0xa2, + 0x50, 0x78, 0x33, 0x71, 0x16, 0xf9, 0x74, 0xed, 0x2a, 0xd8, 0x83, 0x93, 0x8d, 0x29, 0x77, 0x83, + 0xf0, 0x36, 0xd2, 0x50, 0xaf, 0xd1, 0x3f, 0x38, 0xfd, 0xf4, 0xed, 0xfd, 0xda, 0xa8, 0x89, 0x1c, + 0xc7, 0x95, 0x90, 0xd4, 0xab, 0xbf, 0x84, 0xc7, 0xd5, 0xd4, 0xef, 0x12, 0xca, 0x96, 0xf8, 0x43, + 0x38, 0xf8, 0x0f, 0x01, 0x97, 0x07, 0x76, 0x08, 0xac, 0x18, 0x70, 0xfd, 0x6f, 0x04, 0xef, 0xd5, + 0xa6, 0x4a, 0x82, 0x55, 0x44, 0x68, 0x27, 0x44, 0x4a, 0xad, 0x2a, 0xac, 0x77, 0xa3, 0x51, 0xed, + 0x46, 0xed, 0x74, 0x37, 0xeb, 0xa7, 0x7b, 0x35, 0x91, 0x7b, 0xe5, 0x89, 0xfc, 0x1d, 0xc1, 0xb3, + 0xda, 0x52, 0x56, 0x23, 0xf4, 0x25, 0x34, 0x4b, 0xdc, 0x3f, 0x7a, 0x3b, 0xf7, 0x15, 0x01, 0x22, + 0x13, 0xd2, 0xbb, 0xbf, 0xa6, 0x9c, 0x7b, 0x77, 0xb4, 0xa8, 0xaf, 0x43, 0xd4, 0xdc, 0x63, 0xfa, + 0xe5, 0xd1, 0x6c, 0xac, 0x8d, 0xa6, 0xfe, 0x27, 0x82, 0x6e, 0x75, 0xf3, 0x5d, 0xa8, 0x3e, 0x81, + 0x96, 0xd4, 0xad, 0x15, 0xcd, 0xfd, 0xd4, 0x7c, 0x98, 0x62, 0x4d, 0x37, 0x9a, 0xb5, 0xdd, 0xd0, + 0xa0, 0x95, 0xdf, 0x3f, 0x87, 0x58, 0x98, 0x7a, 0x0c, 0x4f, 0x36, 0x65, 0x43, 0xce, 0x3e, 0xfe, + 0x02, 0xda, 0xb9, 0x0c, 0xf0, 0x9c, 0xe1, 0xd3, 0x2d, 0x5a, 0xb3, 0x8a, 0x7d, 0x00, 0x9f, 0xfe, + 0x87, 0x02, 0x27, 0x9b, 0x47, 0xc6, 0x11, 0x13, 0x5b, 0x44, 0xef, 0xeb, 0x75, 0xd1, 0x7b, 0xb1, + 0x4d, 0xf4, 0xd2, 0xad, 0x6a, 0x65, 0xee, 0xff, 0x40, 0xa9, 0xff, 0xb8, 0x8b, 0x1c, 0x1e, 0xc1, + 0xc1, 0x2b, 0x62, 0x5b, 0x97, 0xe5, 0xff, 0x82, 0x8a, 0xac, 0x29, 0xa9, 0xcf, 0xb2, 0x1d, 0x97, + 0x18, 0x97, 0xe6, 0xc4, 0x31, 0x88, 0x71, 0xde, 0x6d, 0xe8, 0x09, 0x68, 0x9b, 0x05, 0xe5, 0xef, + 0x79, 0x9d, 0x2b, 0xaa, 0x3e, 0xcb, 0xaf, 0xa0, 0xc5, 0x64, 0xed, 0x5c, 0x53, 0x64, 0xb7, 0x7a, + 0x0f, 0x41, 0x22, 0x45, 0xc2, 0x74, 0x5f, 0x46, 0x7e, 0xfe, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xcc, 0x3e, 0xe7, 0x96, 0xa1, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index c8342ebcd..577f06879 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -17,6 +17,7 @@ message PushNotificationRegistration { repeated bytes allowed_user_list = 7; repeated bytes blocked_chat_list = 8; bool unregister = 9; + bytes grant = 10; } message PushNotificationRegistrationResponse { @@ -52,6 +53,7 @@ message PushNotificationQueryInfo { string installation_id = 2; bytes public_key = 3; repeated bytes allowed_user_list = 4; + bytes grant = 5; } message PushNotificationQueryResponse { diff --git a/protocol/push_notification_client/push_notification.go b/protocol/push_notification_client/client.go similarity index 87% rename from protocol/push_notification_client/push_notification.go rename to protocol/push_notification_client/client.go index f5c8c229f..2c2c91513 100644 --- a/protocol/push_notification_client/push_notification.go +++ b/protocol/push_notification_client/client.go @@ -341,11 +341,6 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut return nil, err } - marshaledRegistration, err := proto.Marshal(registration) - if err != nil { - return nil, err - } - var serverPublicKeys []*ecdsa.PublicKey for _, server := range servers { @@ -359,6 +354,20 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut return nil, err } + grant, err := c.buildGrantSignature(server.PublicKey, registration.AccessToken) + if err != nil { + c.config.Logger.Error("failed to build grant", zap.Error(err)) + return nil, err + } + + c.config.Logger.Info("GRANT2", zap.Binary("GRANT", grant)) + registration.Grant = grant + + marshaledRegistration, err := proto.Marshal(registration) + if err != nil { + return nil, err + } + // Dispatch message encryptedRegistration, err := c.encryptRegistration(server.PublicKey, marshaledRegistration) if err != nil { @@ -441,6 +450,40 @@ func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotifica return nil } +// buildGrantSignatureMaterial builds a grant for a specific server. +// We use 3 components: +// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid +// 2) The server public key +// 3) The access token +// By verifying this signature, a client can trust the server was instructed to store this access token. + +func (c *Client) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte { + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...) + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...) + signatureMaterial = append(signatureMaterial, []byte(accessToken)...) + return crypto.Keccak256(signatureMaterial) +} + +func (c *Client) buildGrantSignature(serverPublicKey *ecdsa.PublicKey, accessToken string) ([]byte, error) { + signatureMaterial := c.buildGrantSignatureMaterial(&c.config.Identity.PublicKey, serverPublicKey, accessToken) + return crypto.Sign(signatureMaterial, c.config.Identity) +} + +func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, grant []byte, accessToken string) error { + signatureMaterial := c.buildGrantSignatureMaterial(clientPublicKey, serverPublicKey, accessToken) + c.config.Logger.Info("GRANT", zap.Binary("GRANT", grant)) + extractedPublicKey, err := crypto.SigToPub(signatureMaterial, grant) + if err != nil { + return err + } + + if !common.IsPubKeyEqual(clientPublicKey, extractedPublicKey) { + return errors.New("invalid grant") + } + return nil +} + // HandlePushNotificationQueryResponse should update the data in the database for a given user func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { @@ -463,6 +506,14 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ c.config.Logger.Warn("reply for different key, ignoring") continue } + + // We check the user has allowed this server to store this particular + // access token, otherwise anyone could reply with a fake token + // and receive notifications for a user + if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, info.AccessToken); err != nil { + c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err)) + continue + } pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ PublicKey: publicKey, ServerPublicKey: serverPublicKey, diff --git a/protocol/push_notification_client/push_notification_test.go b/protocol/push_notification_client/client_test.go similarity index 100% rename from protocol/push_notification_client/push_notification_test.go rename to protocol/push_notification_client/client_test.go diff --git a/protocol/push_notification_server/errors.go b/protocol/push_notification_server/errors.go index 33a78dc9c..2199fa91b 100644 --- a/protocol/push_notification_server/errors.go +++ b/protocol/push_notification_server/errors.go @@ -8,5 +8,6 @@ var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key") var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences") var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token") +var ErrMalformedPushNotificationRegistrationGrant = errors.New("invalid grant") var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token") var ErrUnknownPushNotificationRegistrationTokenType = errors.New("invalid token type") diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 04d1e56ba..bf0c15468 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -9,6 +9,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/google/uuid" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" @@ -63,7 +64,7 @@ func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) // ValidateRegistration validates a new message against the last one received for a given installationID and and public key // and return the decrypted message -func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) { +func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) { if payload == nil { return nil, ErrEmptyPushNotificationRegistrationPayload } @@ -72,7 +73,7 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrEmptyPushNotificationRegistrationPublicKey } - decryptedPayload, err := p.decryptRegistration(publicKey, payload) + decryptedPayload, err := s.decryptRegistration(publicKey, payload) if err != nil { return nil, err } @@ -87,11 +88,11 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrInvalidPushNotificationRegistrationVersion } - if err := p.validateUUID(registration.InstallationId); err != nil { + if err := s.validateUUID(registration.InstallationId); err != nil { return nil, ErrMalformedPushNotificationRegistrationInstallationID } - previousRegistration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId) + previousRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId) if err != nil { return nil, err } @@ -105,10 +106,20 @@ func (p *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return registration, nil } - if err := p.validateUUID(registration.AccessToken); err != nil { + if err := s.validateUUID(registration.AccessToken); err != nil { return nil, ErrMalformedPushNotificationRegistrationAccessToken } + if len(registration.Grant) == 0 { + return nil, ErrMalformedPushNotificationRegistrationGrant + } + + if err := s.verifyGrantSignature(publicKey, registration.AccessToken, registration.Grant); err != nil { + + s.config.Logger.Error("failed to verify grant", zap.Error(err)) + return nil, ErrMalformedPushNotificationRegistrationGrant + } + if len(registration.Token) == 0 { return nil, ErrMalformedPushNotificationRegistrationDeviceToken } @@ -139,6 +150,7 @@ func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue registration := idAndResponse.Registration info := &protobuf.PushNotificationQueryInfo{ PublicKey: idAndResponse.ID, + Grant: registration.Grant, InstallationId: registration.InstallationId, } @@ -338,3 +350,33 @@ func (p *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) return err } + +// buildGrantSignatureMaterial builds a grant for a specific server. +// We use 3 components: +// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid +// 2) The server public key +// 3) The access token +// By verifying this signature, a client can trust the server was instructed to store this access token. + +func (s *Server) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte { + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...) + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...) + signatureMaterial = append(signatureMaterial, []byte(accessToken)...) + a := crypto.Keccak256(signatureMaterial) + return a +} + +func (s *Server) verifyGrantSignature(clientPublicKey *ecdsa.PublicKey, accessToken string, grant []byte) error { + signatureMaterial := s.buildGrantSignatureMaterial(clientPublicKey, &s.config.Identity.PublicKey, accessToken) + recoveredPublicKey, err := crypto.SigToPub(signatureMaterial, grant) + if err != nil { + return err + } + + if !common.IsPubKeyEqual(recoveredPublicKey, clientPublicKey) { + return errors.New("pubkey mismatch") + } + return nil + +} diff --git a/protocol/push_notification_server/server_test.go b/protocol/push_notification_server/server_test.go index 71ec95a10..4f31070e5 100644 --- a/protocol/push_notification_server/server_test.go +++ b/protocol/push_notification_server/server_test.go @@ -34,6 +34,7 @@ type ServerSuite struct { identity *ecdsa.PrivateKey key *ecdsa.PrivateKey sharedKey []byte + grant []byte server *Server } @@ -64,6 +65,11 @@ func (s *ServerSuite) SetupTest() { sharedKey, err := s.server.generateSharedKey(&s.key.PublicKey) s.Require().NoError(err) s.sharedKey = sharedKey + signatureMaterial := s.server.buildGrantSignatureMaterial(&s.key.PublicKey, &identity.PublicKey, s.accessToken) + grant, err := crypto.Sign(signatureMaterial, s.key) + s.Require().NoError(err) + + s.grant = grant } @@ -104,6 +110,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, Version: 1, }) @@ -118,6 +125,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + Grant: s.grant, InstallationId: "abc", Version: 1, }) @@ -130,6 +138,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + Grant: s.grant, InstallationId: s.installationID, }) s.Require().NoError(err) @@ -142,6 +151,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, @@ -154,6 +164,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Setup persistence s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 2})) @@ -168,6 +179,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, + Grant: s.grant, Unregister: true, Version: 1, }) @@ -181,6 +193,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ InstallationId: s.installationID, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, Version: 1, }) @@ -195,6 +208,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -209,6 +223,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -219,10 +234,41 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) + // Missing grant + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: s.accessToken, + Token: "device-token", + InstallationId: s.installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err) + + // Invalid grant + payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ + AccessToken: s.accessToken, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + Token: "device-token", + Grant: crypto.Keccak256([]byte("invalid")), + InstallationId: s.installationID, + Version: 1, + }) + s.Require().NoError(err) + + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err) + // Missing token type payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, Token: "device-token", + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -237,6 +283,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, @@ -299,6 +346,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Missing installationID payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, Version: 1, }) s.Require().NoError(err) @@ -314,6 +362,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, InstallationId: "abc", + Grant: s.grant, Version: 1, }) cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) @@ -326,6 +375,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Version set to 0 payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, InstallationId: s.installationID, }) s.Require().NoError(err) @@ -340,6 +390,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Version lower than previous one payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -351,6 +402,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Setup persistence s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, InstallationId: s.installationID, Version: 2})) @@ -365,6 +417,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Missing access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ InstallationId: s.installationID, + Grant: s.grant, Version: 1, }) s.Require().NoError(err) @@ -379,6 +432,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Invalid access token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: "bc", + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -394,6 +448,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Missing device token payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, + Grant: s.grant, InstallationId: s.installationID, Version: 1, }) @@ -410,6 +465,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, @@ -461,6 +517,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, Version: 1, @@ -496,6 +553,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { registration := &protobuf.PushNotificationRegistration{ Token: "abc", AccessToken: s.accessToken, + Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, AllowedUserList: allowedUserList, From 7e8d1353d0b035740bc8f16af0cf81dec94eb9e8 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 13 Jul 2020 12:39:33 +0200 Subject: [PATCH 25/46] Create server identity on start if necessary --- .../migrations/migrations.go | 8 +++--- .../sql/1593601728_initial_schema.up.sql | 6 ++++ .../push_notification_server/persistence.go | 28 +++++++++++++++++++ .../persistence_test.go | 26 +++++++++++++++++ protocol/push_notification_server/server.go | 23 +++++++++++++++ services/ext/service.go | 5 +--- 6 files changed, 88 insertions(+), 8 deletions(-) diff --git a/protocol/push_notification_server/migrations/migrations.go b/protocol/push_notification_server/migrations/migrations.go index 07992c252..6e4ecec3f 100644 --- a/protocol/push_notification_server/migrations/migrations.go +++ b/protocol/push_notification_server/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601728_initial_schema.down.sql (200B) -// 1593601728_initial_schema.up.sql (517B) +// 1593601728_initial_schema.up.sql (675B) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601728_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601728_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x90\xc1\x4a\x03\x31\x14\x45\xf7\xf9\x8a\xbb\xec\x40\xff\x60\x56\x99\x98\x62\x20\xbc\xe8\x34\x23\xdd\x85\xda\x46\x7d\x38\xcc\x94\x24\x2d\xfa\xf7\xe2\x28\x38\xad\x9b\x22\x6e\x2f\x8f\x7b\xde\x3d\xaa\xd5\xd2\x6b\x78\xd9\x58\x0d\xb3\x02\x39\x0f\xbd\x31\x6b\xbf\xc6\xe1\x98\x5f\xc2\x30\x16\x7e\xe2\xdd\xb6\xf0\x38\x84\x1c\xd3\x29\xa6\x90\xe2\x33\xe7\x92\xa6\x2c\x63\x21\x80\xc3\xf1\xb1\xe7\x5d\x78\x8d\xef\x68\xac\x6b\xa6\x16\xea\xac\x5d\x0a\x80\x87\x5c\xb6\x7d\xff\xd5\xc0\x7b\x3c\xc8\x56\xdd\xca\xf6\xec\xe6\x14\x53\xe6\x71\x80\x21\x7f\x96\xcf\x49\x53\xf3\x67\xd8\x91\xb9\xef\xf4\xe2\x87\xb9\xbc\x64\x54\x70\x04\xe5\x68\x65\x8d\xf2\x68\xf5\x9d\x95\x4a\x8b\xaa\x16\xe2\x7b\xae\xa1\x1b\xbd\x01\xef\xdf\xc2\x75\x23\xc3\x6c\xa0\xa3\x2b\xcd\xcc\x3e\xac\xea\x7f\x20\x87\x4b\x93\x7f\xf9\xe4\xb7\xab\x5a\x88\x8f\x00\x00\x00\xff\xff\x5c\xf8\x30\xd8\x05\x02\x00\x00") +var __1593601728_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x91\x31\x6b\xc3\x30\x14\x84\x77\xfd\x8a\x37\xc6\x90\xa1\xbb\x27\xd9\x91\xa9\x40\x48\xad\x23\x97\x6c\xc2\xb5\xd5\xe6\x51\x23\x07\x49\x31\xf5\xbf\x2f\x71\x86\x2a\x69\x87\x10\xb2\x1e\x8f\xbb\xf7\xdd\x95\x35\xa3\x9a\x81\xa6\x85\x60\xc0\x2b\x90\x4a\x03\xdb\xf1\xad\xde\xc2\xe1\x18\xf6\xc6\x8d\x11\x3f\xb0\x6b\x23\x8e\xce\x04\xeb\x27\xeb\x8d\xb7\x9f\x18\xa2\x5f\xb4\x00\x2b\x02\x70\x38\xbe\x0f\xd8\x99\x2f\x3b\x43\x21\x54\xb1\xb8\xc8\x46\x88\x35\x01\x40\x17\x62\x3b\x0c\x67\x07\xec\xe1\x8d\xd6\xe5\x33\xad\x2f\x6e\x26\xeb\x03\x8e\x0e\xb8\xd4\x17\x7a\x9a\xb4\x38\x9f\xc4\x46\xf2\xd7\x86\xad\x7e\x33\xd7\xd7\x19\x19\x28\x09\xa5\x92\x95\xe0\xa5\x86\x9a\xbd\x08\x5a\x32\x92\xe5\x84\xdc\x83\x8b\xbd\x75\x11\xe3\x7c\x26\xf5\x38\xb5\xd1\xfe\x8f\x1a\x66\x17\xf7\x36\x62\x77\xe2\x4c\x59\x60\xc3\x2a\xda\x08\x0d\x4f\x09\x40\x7a\x9d\xa5\xdf\x71\xb9\x61\x3b\xc0\xfe\xdb\xdc\x36\x81\x49\xea\x57\xf2\xc6\xdd\x92\xfe\xb2\xfc\x01\xc9\xe6\x7a\xe7\x7b\x3e\xf9\xbb\x64\x4e\xc8\x4f\x00\x00\x00\xff\xff\xcc\xa0\x4d\x54\xa3\x02\x00\x00") func _1593601728_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601728_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 517, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf1, 0x55, 0xab, 0xbf, 0xee, 0x66, 0xe9, 0x81, 0x9f, 0x89, 0xca, 0x6b, 0xf8, 0x1d, 0x12, 0x59, 0xf6, 0x1e, 0xac, 0x79, 0x93, 0x8f, 0x47, 0xbd, 0x76, 0x65, 0xbb, 0x10, 0x99, 0x8a, 0xda, 0x9e}} + info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1594636412, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}} return a, nil } diff --git a/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql b/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql index 0c8f38e4c..d5020d7b2 100644 --- a/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql +++ b/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql @@ -6,6 +6,12 @@ CREATE TABLE IF NOT EXISTS push_notification_server_registrations ( UNIQUE(public_key, installation_id) ON CONFLICT REPLACE ); +CREATE TABLE IF NOT EXISTS push_notification_server_identity ( + private_key BLOB NOT NULL, + synthetic_id INT NOT NULL DEFAULT 0, + UNIQUE(synthetic_id) +); + CREATE INDEX idx_push_notification_server_registrations_public_key ON push_notification_server_registrations(public_key); CREATE INDEX idx_push_notification_server_registrations_public_key_installation_id ON push_notification_server_registrations(public_key, installation_id); diff --git a/protocol/push_notification_server/persistence.go b/protocol/push_notification_server/persistence.go index 1d6eebe34..a4d039a0b 100644 --- a/protocol/push_notification_server/persistence.go +++ b/protocol/push_notification_server/persistence.go @@ -1,11 +1,13 @@ package push_notification_server import ( + "crypto/ecdsa" "database/sql" "strings" "github.com/golang/protobuf/proto" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/protocol/protobuf" ) @@ -21,6 +23,10 @@ type Persistence interface { DeletePushNotificationRegistration(publicKey []byte, installationID string) error // SavePushNotificationRegistration saves a push notification option to the db SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error + // GetIdentity returns the server identity key + GetIdentity() (*ecdsa.PrivateKey, error) + // SaveIdentity saves the server identity key + SaveIdentity(*ecdsa.PrivateKey) error } type SQLitePersistence struct { @@ -124,3 +130,25 @@ func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey []byte, _, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID) return err } + +func (p *SQLitePersistence) SaveIdentity(privateKey *ecdsa.PrivateKey) error { + _, err := p.db.Exec(`INSERT INTO push_notification_server_identity (private_key) VALUES (?)`, crypto.FromECDSA(privateKey)) + return err +} + +func (p *SQLitePersistence) GetIdentity() (*ecdsa.PrivateKey, error) { + var pkBytes []byte + err := p.db.QueryRow(`SELECT private_key FROM push_notification_server_identity LIMIT 1`).Scan(&pkBytes) + if err == sql.ErrNoRows { + return nil, nil + } + if err != nil { + return nil, err + } + + pk, err := crypto.ToECDSA(pkBytes) + if err != nil { + return nil, err + } + return pk, nil +} diff --git a/protocol/push_notification_server/persistence_test.go b/protocol/push_notification_server/persistence_test.go index 0f398f567..e81204cab 100644 --- a/protocol/push_notification_server/persistence_test.go +++ b/protocol/push_notification_server/persistence_test.go @@ -57,3 +57,29 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() { s.Require().True(proto.Equal(registration, retrievedRegistration)) } + +func (s *SQLitePersistenceSuite) TestSaveAndRetrieveIdentity() { + retrievedKey, err := s.persistence.GetIdentity() + s.Require().NoError(err) + s.Require().Nil(retrievedKey) + + key, err := crypto.GenerateKey() + s.Require().NoError(err) + s.Require().NoError(s.persistence.SaveIdentity(key)) + + retrievedKey, err = s.persistence.GetIdentity() + s.Require().NoError(err) + + s.Require().Equal(key, retrievedKey) +} + +func (s *SQLitePersistenceSuite) TestSaveDifferentIdenities() { + key1, err := crypto.GenerateKey() + s.Require().NoError(err) + key2, err := crypto.GenerateKey() + s.Require().NoError(err) + + // First one should be successul, second should fail + s.Require().NoError(s.persistence.SaveIdentity(key1)) + s.Require().Error(s.persistence.SaveIdentity(key2)) +} diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index bf0c15468..be0b1b286 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -17,6 +17,7 @@ import ( ) const encryptedPayloadKeyLength = 16 +const defaultGorushURL = "https://gorush.status.im" type Config struct { // Identity is our identity key @@ -34,6 +35,10 @@ type Server struct { } func New(config *Config, persistence Persistence, messageProcessor *common.MessageProcessor) *Server { + if len(config.GorushURL) == 0 { + config.GorushURL = defaultGorushURL + + } return &Server{persistence: persistence, config: config, messageProcessor: messageProcessor} } @@ -270,6 +275,24 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, } func (s *Server) Start() error { + if s.config.Identity == nil { + // Pull identity from database + identity, err := s.persistence.GetIdentity() + if err != nil { + return err + } + if identity == nil { + identity, err = crypto.GenerateKey() + if err != nil { + return err + } + if err := s.persistence.SaveIdentity(identity); err != nil { + return err + } + } + s.config.Identity = identity + } + pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys() if err != nil { return err diff --git a/services/ext/service.go b/services/ext/service.go index 5346892fa..2e5e9669c 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -459,12 +459,9 @@ func buildMessengerOptions( options = append(options, protocol.WithDatasync()) } - // For now build with default/hardcoded options. if config.PushNotificationServerEnabled { config := &push_notification_server.Config{ - Identity: identity, - Logger: logger, - GorushURL: "https://gorush.status.im", + Logger: logger, } options = append(options, protocol.WithPushNotificationServerConfig(config)) } From c88df6c1c787ebe948b57f41b5f05b63cae6bf9c Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 14 Jul 2020 16:07:19 +0200 Subject: [PATCH 26/46] Handle messages sent to all devices --- protocol/messenger.go | 23 +++ protocol/push_notification_client/client.go | 132 ++++++++++++++---- .../push_notification_client/persistence.go | 62 +++++++- protocol/push_notification_server/gorush.go | 1 - protocol/push_notification_server/server.go | 41 ++++-- services/ext/api.go | 27 ++++ 6 files changed, 240 insertions(+), 46 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 5a8378f1a..7d98ef316 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3,6 +3,7 @@ package protocol import ( "context" "crypto/ecdsa" + "database/sql" "github.com/status-im/status-go/protocol/common" "io/ioutil" "math/rand" @@ -74,6 +75,7 @@ type Messenger struct { modifiedInstallations map[string]bool installationID string mailserver []byte + database *sql.DB mutex sync.Mutex } @@ -248,6 +250,7 @@ func NewMessenger( var pushNotificationServer *push_notification_server.Server if c.pushNotificationServerConfig != nil { + c.pushNotificationServerConfig.Identity = identity pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(database) pushNotificationServer = push_notification_server.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor) } @@ -285,6 +288,7 @@ func NewMessenger( modifiedInstallations: make(map[string]bool), messagesPersistenceEnabled: c.messagesPersistenceEnabled, verifyTransactionClient: c.verifyTransactionClient, + database: database, shutdownTasks: []func() error{ database.Close, pushNotificationClient.Stop, @@ -304,6 +308,7 @@ func NewMessenger( } func (m *Messenger) Start() error { + m.logger.Info("starting messenger", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey)))) // Start push notification server if m.pushNotificationServer != nil { if err := m.pushNotificationServer.Start(); err != nil { @@ -3065,6 +3070,24 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) } +func (m *Messenger) StartPushNotificationServer() error { + if m.pushNotificationServer == nil { + pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(m.database) + config := &push_notification_server.Config{ + Logger: m.logger, + Identity: m.identity, + } + m.pushNotificationServer = push_notification_server.New(config, pushNotificationServerPersistence, m.processor) + } + + return m.pushNotificationServer.Start() +} + +func (m *Messenger) StopPushNotificationServer() error { + m.pushNotificationServer = nil + return nil +} + func generateAliasAndIdenticon(pk string) (string, string, error) { identicon, err := identicon.GenerateBase64(pk) if err != nil { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 2c2c91513..fdc75dc61 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -1,17 +1,17 @@ package push_notification_client import ( + "bytes" "context" "crypto/aes" "crypto/cipher" - "sort" - - "bytes" "crypto/ecdsa" "crypto/rand" "encoding/hex" + "encoding/json" "errors" "io" + "sort" "time" "github.com/golang/protobuf/proto" @@ -19,6 +19,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "go.uber.org/zap" @@ -29,10 +30,23 @@ const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 type PushNotificationServer struct { - PublicKey *ecdsa.PublicKey - Registered bool - RegisteredAt int64 - AccessToken string + PublicKey *ecdsa.PublicKey `json:"-"` + Registered bool `json:"registered,omitempty"` + RegisteredAt int64 `json:"registeredAt,omitempty"` + AccessToken string `json:"accessToken,omitempty"` +} + +func (s *PushNotificationServer) MarshalJSON() ([]byte, error) { + type ServerAlias PushNotificationServer + item := struct { + *ServerAlias + PublicKeyString string `json:"publicKey"` + }{ + ServerAlias: (*ServerAlias)(s), + PublicKeyString: types.EncodeHex(crypto.FromECDSAPub(s.PublicKey)), + } + + return json.Marshal(item) } type PushNotificationInfo struct { @@ -104,7 +118,12 @@ func (c *Client) Start() error { subscription := c.messageProcessor.Subscribe() for { select { - case m := <-subscription: + case m, more := <-subscription: + if !more { + c.config.Logger.Info("no more") + return + } + c.config.Logger.Info("handling message sent") if err := c.HandleMessageSent(m); err != nil { c.config.Logger.Error("failed to handle message", zap.Error(err)) } @@ -121,28 +140,46 @@ func (c *Client) Stop() error { return nil } -type notificationSendingSpec struct { - serverPublicKey *ecdsa.PublicKey - installationID string - messageID []byte -} - // The message has been sent // We should: // 1) Check whether we should notify on anything // 2) Refresh info if necessaary // 3) Sent push notifications +// TODO: handle DH messages func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { + c.config.Logger.Info("sent message", zap.Any("sent message", sentMessage)) if !c.config.SendEnabled { + c.config.Logger.Info("send not enabled, ignoring") return nil } publicKey := sentMessage.PublicKey + // Check we track this messages fist + var trackedMessageIDs [][]byte + + for _, messageID := range sentMessage.MessageIDs { + tracked, err := c.persistence.TrackedMessage(messageID) + if err != nil { + return err + } + if tracked { + trackedMessageIDs = append(trackedMessageIDs, messageID) + } + } + + // Nothing to do + if len(trackedMessageIDs) == 0 { + return nil + } + + sendToAllDevices := len(sentMessage.Spec.Installations) == 0 + var installationIDs []string - var notificationSpecs []*notificationSendingSpec + anyActionableMessage := sendToAllDevices + c.config.Logger.Info("send to all devices", zap.Bool("send to all", sendToAllDevices)) - //Find if there's any actionable message - for _, messageID := range sentMessage.MessageIDs { + // Collect installationIDs + for _, messageID := range trackedMessageIDs { for _, installation := range sentMessage.Spec.Installations { installationID := installation.ID shouldNotify, err := c.shouldNotifyOn(publicKey, installationID, messageID) @@ -150,20 +187,20 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { return err } if shouldNotify { - notificationSpecs = append(notificationSpecs, ¬ificationSendingSpec{ - installationID: installationID, - messageID: messageID, - }) + anyActionableMessage = true installationIDs = append(installationIDs, installation.ID) } } } // Is there anything we should be notifying on? - if len(installationIDs) == 0 { + if !anyActionableMessage { + c.config.Logger.Info("no actionable installation IDs") return nil } + c.config.Logger.Info("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) + // Check if we queried recently queriedAt, err := c.persistence.GetQueriedAt(publicKey) if err != nil { @@ -173,8 +210,10 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { // Naively query again if too much time has passed. // Here it might not be necessary if time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { + c.config.Logger.Info("querying info") err := c.QueryPushNotificationInfo(publicKey) if err != nil { + c.config.Logger.Error("could not query pn info", zap.Error(err)) return err } // This is just horrible, but for now will do, @@ -188,9 +227,12 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { time.Sleep(3 * time.Second) } + + c.config.Logger.Info("queried info") // Retrieve infos info, err := c.GetPushNotificationInfo(publicKey, installationIDs) if err != nil { + c.config.Logger.Error("could not get pn info", zap.Error(err)) return err } @@ -203,10 +245,14 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { return info[i].ServerPublicKey.X.Cmp(info[j].ServerPublicKey.X) <= 0 }) + c.config.Logger.Info("retrieved info") + installationIDsMap := make(map[string]bool) // One info per installation id, grouped by server actionableInfos := make(map[string][]*PushNotificationInfo) for _, i := range info { + + c.config.Logger.Info("queried info", zap.String("id", i.InstallationID)) if !installationIDsMap[i.InstallationID] { serverKey := hex.EncodeToString(crypto.CompressPubkey(i.ServerPublicKey)) actionableInfos[serverKey] = append(actionableInfos[serverKey], i) @@ -215,6 +261,8 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { } + c.config.Logger.Info("actionable info", zap.Int("count", len(actionableInfos))) + for _, infos := range actionableInfos { var pushNotifications []*protobuf.PushNotification for _, i := range infos { @@ -227,7 +275,7 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { } request := &protobuf.PushNotificationRequest{ - MessageId: sentMessage.MessageIDs[0], + MessageId: trackedMessageIDs[0], Requests: pushNotifications, } serverPublicKey := infos[0].ServerPublicKey @@ -248,6 +296,20 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { if err != nil { return err } + + // Mark message as sent, this is at-most-once semantic + // for all messageIDs + for _, i := range infos { + for _, messageID := range trackedMessageIDs { + + c.config.Logger.Info("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) + if err := c.notifiedOn(publicKey, i.InstallationID, messageID); err != nil { + return err + } + + } + } + } return nil @@ -259,7 +321,11 @@ func (c *Client) NotifyOnMessageID(chatID string, messageID []byte) error { } func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { - return c.persistence.ShouldSentNotificationFor(publicKey, installationID, messageID) + if len(installationID) == 0 { + return c.persistence.ShouldSendNotificationToAllInstallationIDs(publicKey, messageID) + } else { + return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID) + } } func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { @@ -395,7 +461,7 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut case <-c.quit: return servers, nil case <-ctx.Done(): - c.config.Logger.Debug("Context done") + c.config.Logger.Info("Context done") return servers, nil case <-time.After(200 * time.Millisecond): servers, err = c.persistence.GetServersByPublicKey(serverPublicKeys) @@ -422,7 +488,7 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, response protobuf.PushNotificationRegistrationResponse) error { - c.config.Logger.Debug("received push notification registration response", zap.Any("response", response)) + c.config.Logger.Info("received push notification registration response", zap.Any("response", response)) // TODO: handle non successful response and match request id // Not successful ignore for now if !response.Success { @@ -487,7 +553,7 @@ func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey * // HandlePushNotificationQueryResponse should update the data in the database for a given user func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { - c.config.Logger.Debug("received push notification query response", zap.Any("response", response)) + c.config.Logger.Info("received push notification query response", zap.Any("response", response)) if len(response.Info) == 0 { return errors.New("empty response from the server") } @@ -497,7 +563,7 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ return err } if publicKey == nil { - c.config.Logger.Debug("query not found") + c.config.Logger.Info("query not found") return nil } var pushNotificationInfo []*PushNotificationInfo @@ -538,7 +604,7 @@ func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationRe } func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { - c.config.Logger.Debug("adding push notification server", zap.Any("public-key", publicKey)) + c.config.Logger.Info("adding push notification server", zap.Any("public-key", publicKey)) currentServers, err := c.persistence.GetServers() if err != nil { return err @@ -571,7 +637,7 @@ func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { } encodedPublicKey := hex.EncodeToString(hashedPublicKey) - c.config.Logger.Debug("sending query") + c.config.Logger.Info("sending query") messageID, err := c.messageProcessor.SendPublic(context.Background(), encodedPublicKey, rawMessage) if err != nil { @@ -582,7 +648,11 @@ func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { } func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) { - return c.persistence.GetPushNotificationInfo(publicKey, installationIDs) + if len(installationIDs) == 0 { + return c.persistence.GetPushNotificationInfoByPublicKey(publicKey) + } else { + return c.persistence.GetPushNotificationInfo(publicKey, installationIDs) + } } func (c *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index a4ca30a90..60e265184 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -24,6 +24,20 @@ func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) err return err } +func (p *Persistence) TrackedMessage(messageID []byte) (bool, error) { + var count uint64 + err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count) + if err != nil { + return false, err + } + + if count == 0 { + return false, nil + } + + return true, nil +} + func (p *Persistence) SavePushNotificationQuery(publicKey *ecdsa.PublicKey, queryID []byte) error { queriedAt := time.Now().Unix() _, err := p.db.Exec(`INSERT INTO push_notification_client_queries (public_key, query_id, queried_at) VALUES (?,?,?)`, crypto.CompressPubkey(publicKey), queryID, queriedAt) @@ -114,7 +128,33 @@ func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, instal return infos, nil } -func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { +func (p *Persistence) GetPushNotificationInfoByPublicKey(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) { + rows, err := p.db.Query(`SELECT server_public_key, installation_id, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ?`, crypto.CompressPubkey(publicKey)) + if err != nil { + return nil, err + } + var infos []*PushNotificationInfo + for rows.Next() { + var serverPublicKeyBytes []byte + info := &PushNotificationInfo{PublicKey: publicKey} + err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.AccessToken, &info.RetrievedAt) + if err != nil { + return nil, err + } + + serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes) + if err != nil { + return nil, err + } + + info.ServerPublicKey = serverPublicKey + infos = append(infos, info) + } + + return infos, nil +} + +func (p *Persistence) ShouldSendNotificationFor(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { // First we check that we are tracking this message, next we check that we haven't already sent this var count uint64 err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count) @@ -134,6 +174,26 @@ func (p *Persistence) ShouldSentNotificationFor(publicKey *ecdsa.PublicKey, inst return count == 0, nil } +func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) { + // First we check that we are tracking this message, next we check that we haven't already sent this + var count uint64 + err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count) + if err != nil { + return false, err + } + + if count == 0 { + return false, nil + } + + err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? `, messageID, crypto.CompressPubkey(publicKey)).Scan(&count) + if err != nil { + return false, err + } + + return count == 0, nil +} + func (p *Persistence) NotifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { sentAt := time.Now().Unix() _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, sent_at) VALUES (?, ?, ?, ?)`, crypto.CompressPubkey(publicKey), installationID, messageID, sentAt) diff --git a/protocol/push_notification_server/gorush.go b/protocol/push_notification_server/gorush.go index 90a515b89..ff88a0205 100644 --- a/protocol/push_notification_server/gorush.go +++ b/protocol/push_notification_server/gorush.go @@ -75,5 +75,4 @@ func sendGoRushNotification(request *GoRushRequest, url string) error { return err } return nil - } diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index be0b1b286..171fcb5e5 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -11,6 +11,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "go.uber.org/zap" @@ -136,17 +137,17 @@ func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return registration, nil } -func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { +func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { - p.config.Logger.Debug("handling push notification query") + s.config.Logger.Info("handling push notification query") response := &protobuf.PushNotificationQueryResponse{} if query == nil || len(query.PublicKeys) == 0 { return response } - registrations, err := p.persistence.GetPushNotificationRegistrationByPublicKeys(query.PublicKeys) + registrations, err := s.persistence.GetPushNotificationRegistrationByPublicKeys(query.PublicKeys) if err != nil { - // TODO: log errors + s.config.Logger.Error("failed to retrieve registration", zap.Error(err)) return response } @@ -171,10 +172,12 @@ func (p *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue return response } -func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { +func (s *Server) HandlePushNotificationRequest(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { + s.config.Logger.Info("handling pn request") response := &protobuf.PushNotificationResponse{} // We don't even send a response in this case if request == nil || len(request.MessageId) == 0 { + s.config.Logger.Warn("empty message id") return nil } @@ -185,18 +188,20 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio var requestAndRegistrations []*RequestAndRegistration for _, pn := range request.Requests { - registration, err := p.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) + registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) report := &protobuf.PushNotificationReport{ PublicKey: pn.PublicKey, InstallationId: pn.InstallationId, } if err != nil { - // TODO: log error + s.config.Logger.Error("failed to retrieve registration", zap.Error(err)) report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE } else if registration == nil { + s.config.Logger.Warn("empty registration") report.Error = protobuf.PushNotificationReport_NOT_REGISTERED } else if registration.AccessToken != pn.AccessToken { + s.config.Logger.Warn("invalid access token") report.Error = protobuf.PushNotificationReport_WRONG_TOKEN } else { // For now we just assume that the notification will be successful @@ -210,14 +215,19 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio response.Reports = append(response.Reports, report) } + s.config.Logger.Info("built pn request") if len(requestAndRegistrations) == 0 { + s.config.Logger.Warn("no request and registration") return response } // This can be done asynchronously goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) - err := sendGoRushNotification(goRushRequest, p.config.GorushURL) + //TODO: REMOVE ME + s.config.Logger.Info("REQUEST", zap.Any("REQUEST", goRushRequest)) + err := sendGoRushNotification(goRushRequest, s.config.GorushURL) if err != nil { + s.config.Logger.Error("failed to send go rush notification", zap.Error(err)) // TODO: handle this error? } @@ -226,7 +236,7 @@ func (p *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { - s.config.Logger.Debug("handling push notification registration") + s.config.Logger.Info("handling push notification registration") response := &protobuf.PushNotificationRegistrationResponse{ RequestId: common.Shake256(payload), } @@ -269,13 +279,15 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, } response.Success = true - s.config.Logger.Debug("handled push notification registration successfully") + s.config.Logger.Info("handled push notification registration successfully") return response } func (s *Server) Start() error { + s.config.Logger.Info("starting push notification server") if s.config.Identity == nil { + s.config.Logger.Info("Identity nil") // Pull identity from database identity, err := s.persistence.GetIdentity() if err != nil { @@ -303,6 +315,8 @@ func (s *Server) Start() error { } } + s.config.Logger.Info("started push notification server", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&s.config.Identity.PublicKey)))) + return nil } @@ -354,9 +368,10 @@ func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, messag } -func (p *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, +func (s *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, request protobuf.PushNotificationRequest) error { - response := p.HandlePushNotificationRequest(&request) + s.config.Logger.Info("handling pn request") + response := s.HandlePushNotificationRequest(&request) if response == nil { return nil } @@ -370,7 +385,7 @@ func (p *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, } - _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) return err } diff --git a/services/ext/api.go b/services/ext/api.go index 00850e213..11968a44d 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -12,10 +12,12 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/rlp" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/protocol" "github.com/status-im/status-go/protocol/encryption/multidevice" + "github.com/status-im/status-go/protocol/push_notification_client" "github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/services/ext/mailservers" ) @@ -401,6 +403,31 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error { return api.service.UpdateMailservers(nodes) } +// PushNotifications server + +func (api *PublicAPI) StartPushNotificationServer() error { + return api.service.messenger.StartPushNotificationServer() +} + +func (api *PublicAPI) StopPushNotificationServer() error { + return api.service.messenger.StopPushNotificationServer() +} + +// PushNotification client + +func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { + return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken) +} + +func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { + publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return err + } + + return api.service.messenger.AddPushNotificationServer(ctx, publicKey) +} + // Echo is a method for testing purposes. func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) { return message, nil From e89e36b08103748e4848b314479c5420f4e1d38d Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 08:31:39 +0200 Subject: [PATCH 27/46] Use resend automatically for datasync --- protocol/common/message_processor.go | 2 +- protocol/messenger.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 495e6f715..738b5ca00 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -160,7 +160,7 @@ func (p *MessageProcessor) sendPrivate( messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) - if p.featureFlags.Datasync { + if p.featureFlags.Datasync && rawMessage.ResendAutomatically { if err := p.addToDataSync(recipient, wrappedMessage); err != nil { return nil, errors.Wrap(err, "failed to send message with datasync") } diff --git a/protocol/messenger.go b/protocol/messenger.go index 7d98ef316..fea068707 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1201,10 +1201,11 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) err } _, err = m.dispatchMessage(ctx, &common.RawMessage{ - LocalChatID: chat.ID, - Payload: message.Payload, - MessageType: message.MessageType, - Recipients: message.Recipients, + LocalChatID: chat.ID, + Payload: message.Payload, + MessageType: message.MessageType, + Recipients: message.Recipients, + ResendAutomatically: message.ResendAutomatically, }) return err } @@ -1402,9 +1403,10 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } id, err := m.dispatchMessage(ctx, &common.RawMessage{ - LocalChatID: chat.ID, - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, + LocalChatID: chat.ID, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, + ResendAutomatically: true, }) if err != nil { return nil, err From 6f8c3025f3ed8e053f2665b2bc313970a34b495e Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 09:23:31 +0200 Subject: [PATCH 28/46] Retrieve and add push notification registration --- protocol/push_notification_client/client.go | 169 ++++++++++++------ .../push_notification_client/client_test.go | 21 +++ .../migrations/migrations.go | 8 +- .../sql/1593601729_initial_schema.up.sql | 6 + .../push_notification_client/persistence.go | 32 ++++ .../persistence_test.go | 28 +++ 6 files changed, 203 insertions(+), 61 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index fdc75dc61..b2a895fa9 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -84,8 +84,8 @@ type Client struct { quit chan struct{} config *Config - // lastPushNotificationVersion is the latest known push notification version - lastPushNotificationVersion uint64 + // lastPushNotificationRegistration is the latest known push notification version + lastPushNotificationRegistration *protobuf.PushNotificationRegistration // AccessToken is the access token that is currently being used AccessToken string @@ -109,11 +109,7 @@ func New(persistence *Persistence, config *Config, processor *common.MessageProc reader: rand.Reader} } -func (c *Client) Start() error { - if c.messageProcessor == nil { - return errors.New("can't start, missing message processor") - } - +func (c *Client) subscribeForSentMessages() { go func() { subscription := c.messageProcessor.Subscribe() for { @@ -132,6 +128,33 @@ func (c *Client) Start() error { } } }() + +} + +func (c *Client) loadLastPushNotificationRegistration() error { + lastRegistration, err := c.persistence.GetLastPushNotificationRegistration() + if err != nil { + return err + } + if lastRegistration == nil { + lastRegistration = &protobuf.PushNotificationRegistration{} + } + c.lastPushNotificationRegistration = lastRegistration + return nil + +} + +func (c *Client) Start() error { + if c.messageProcessor == nil { + return errors.New("can't start, missing message processor") + } + + err := c.loadLastPushNotificationRegistration() + if err != nil { + return err + } + c.subscribeForSentMessages() + return nil } @@ -371,26 +394,95 @@ func (p *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([ return encryptedTokens, nil } -func (p *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { - token := uuid.New().String() - allowedUserList, err := p.allowedUserList([]byte(token), contactIDs) +func (p *Client) getToken() string { + return uuid.New().String() + +} +func (c *Client) getVersion() uint64 { + if c.lastPushNotificationRegistration == nil { + return 1 + } + return c.lastPushNotificationRegistration.Version + 1 +} + +func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { + token := c.getToken() + allowedUserList, err := c.allowedUserList([]byte(token), contactIDs) if err != nil { return nil, err } options := &protobuf.PushNotificationRegistration{ AccessToken: token, - TokenType: p.config.TokenType, - Version: p.lastPushNotificationVersion + 1, - InstallationId: p.config.InstallationID, - Token: p.DeviceToken, - Enabled: p.config.RemoteNotificationsEnabled, - BlockedChatList: p.mutedChatIDsHashes(mutedChatIDs), + TokenType: c.config.TokenType, + Version: c.getVersion(), + InstallationId: c.config.InstallationID, + Token: c.DeviceToken, + Enabled: c.config.RemoteNotificationsEnabled, + BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), AllowedUserList: allowedUserList, } return options, nil } +// shouldRefreshToken tells us whether we should pull a new token, that's only necessary when a contact is removed +func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.PublicKey) bool { + newContactIDsMap := make(map[string]bool) + for _, pk := range newContactIDs { + newContactIDsMap[types.EncodeHex(crypto.FromECDSAPub(pk))] = true + } + + for _, pk := range oldContactIDs { + if ok := newContactIDsMap[types.EncodeHex(crypto.FromECDSAPub(pk))]; !ok { + return true + } + + } + return false +} + +func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegistration, server *PushNotificationServer) error { + // Reset server registration data + server.Registered = false + server.RegisteredAt = 0 + server.AccessToken = registration.AccessToken + + if err := c.persistence.UpsertServer(server); err != nil { + return err + } + + grant, err := c.buildGrantSignature(server.PublicKey, registration.AccessToken) + if err != nil { + c.config.Logger.Error("failed to build grant", zap.Error(err)) + return err + } + + registration.Grant = grant + + marshaledRegistration, err := proto.Marshal(registration) + if err != nil { + return err + } + + // Dispatch message + encryptedRegistration, err := c.encryptRegistration(server.PublicKey, marshaledRegistration) + if err != nil { + return err + } + rawMessage := &common.RawMessage{ + Payload: encryptedRegistration, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION, + } + + _, err = c.messageProcessor.SendPrivate(context.Background(), server.PublicKey, rawMessage) + + if err != nil { + return err + } + return nil + +} + func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]*PushNotificationServer, error) { c.DeviceToken = deviceToken servers, err := c.persistence.GetServers() @@ -409,47 +501,11 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut var serverPublicKeys []*ecdsa.PublicKey for _, server := range servers { - - // Reset server registration data - server.Registered = false - server.RegisteredAt = 0 - server.AccessToken = registration.AccessToken + err := c.registerWithServer(registration, server) + if err != nil { + return nil, err + } serverPublicKeys = append(serverPublicKeys, server.PublicKey) - - if err := c.persistence.UpsertServer(server); err != nil { - return nil, err - } - - grant, err := c.buildGrantSignature(server.PublicKey, registration.AccessToken) - if err != nil { - c.config.Logger.Error("failed to build grant", zap.Error(err)) - return nil, err - } - - c.config.Logger.Info("GRANT2", zap.Binary("GRANT", grant)) - registration.Grant = grant - - marshaledRegistration, err := proto.Marshal(registration) - if err != nil { - return nil, err - } - - // Dispatch message - encryptedRegistration, err := c.encryptRegistration(server.PublicKey, marshaledRegistration) - if err != nil { - return nil, err - } - rawMessage := &common.RawMessage{ - Payload: encryptedRegistration, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION, - } - - _, err = c.messageProcessor.SendPrivate(context.Background(), server.PublicKey, rawMessage) - - if err != nil { - return nil, err - } - } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -538,7 +594,6 @@ func (c *Client) buildGrantSignature(serverPublicKey *ecdsa.PublicKey, accessTok func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, grant []byte, accessToken string) error { signatureMaterial := c.buildGrantSignatureMaterial(clientPublicKey, serverPublicKey, accessToken) - c.config.Logger.Info("GRANT", zap.Binary("GRANT", grant)) extractedPublicKey, err := crypto.SigToPub(signatureMaterial, grant) if err != nil { return err diff --git a/protocol/push_notification_client/client_test.go b/protocol/push_notification_client/client_test.go index 852ea6ec1..0306fe044 100644 --- a/protocol/push_notification_client/client_test.go +++ b/protocol/push_notification_client/client_test.go @@ -156,3 +156,24 @@ func (s *ClientSuite) TestNotifyOnMessageID() { s.Require().NoError(err) s.Require().False(response) } + +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 + s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey})) + + // everything the same + s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey})) + + // A contact is removed + s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey})) + +} diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index 7752d8ed8..e4c2c0cd3 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.284kB) +// 1593601729_initial_schema.up.sql (1.474kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x92\x4f\x8f\xaa\x30\x14\xc5\xf7\x7c\x8a\xbb\x94\x84\xc5\xdb\xbb\x02\x2c\x2f\x24\x4d\xfb\x9e\x96\xc4\x5d\xd3\x29\x57\x6d\x64\xc0\x69\xab\x19\xbf\xfd\xc4\xff\x22\x0e\x4e\x1c\x37\x24\xb4\xa7\xf7\xde\xdf\x3d\x27\x1d\x93\x58\x10\x10\x71\x42\x09\xe4\x19\x30\x2e\x80\x4c\xf3\x89\x98\xc0\x6a\xed\x16\xb2\x6e\xbc\x99\x19\xad\xbc\x69\x6a\xa9\x2b\x83\xb5\x97\x0e\xed\x06\xad\x83\x41\x00\xb0\x5a\xbf\x55\x46\xcb\x25\x6e\x21\xa1\x3c\xd9\xbf\x67\x05\xa5\x51\x00\x60\x71\x6e\x9c\x47\x8b\x25\x24\x9c\x53\x12\x33\x18\x91\x2c\x2e\xa8\x80\x2c\xa6\x13\xd2\xd6\x48\xe5\x21\x67\xe2\x5c\xe1\xac\xfd\xb3\xd3\x29\xad\xd1\x39\xe9\x9b\x25\xd6\x20\xc8\x54\xec\x0e\x0b\x96\xff\x2f\xc8\xe0\x32\x43\x08\x9c\x41\xca\x59\x46\xf3\x54\xc0\x98\xfc\xa3\x71\x4a\x82\x70\x18\x04\xcf\x70\x7e\xac\xd1\x1a\x7c\xcc\x79\xd0\x75\x00\x4e\x57\x5b\x69\xca\xee\xa3\xce\xec\xd1\x49\xfb\x5a\x08\x53\xcf\x9a\x87\x04\x07\x47\x65\x9f\xc4\xd4\xce\xab\xaa\x3a\xd4\x36\xe5\xde\x83\x96\xa0\xe3\xd0\x4d\x16\xbc\x35\xb8\xb9\xbf\xa5\xee\x2e\x6e\xdb\x45\xdd\x11\x5f\xbb\x26\x6f\x95\x5e\x62\x29\xdf\xd1\x39\x35\x3f\x9a\x7e\xfc\xb9\xeb\x9f\x5e\x28\x7f\x77\x0f\xa7\x4a\xdf\x73\x5e\xca\xb6\x19\xf2\xbf\x8c\x8f\x49\x00\xf0\x2c\x84\xdb\x7d\xae\x2f\x1e\x63\xfc\xca\xf2\x7d\xbf\x9f\x70\x46\xd0\xe3\x6d\xd8\x26\xce\xd9\x88\x4c\xc1\x94\x9f\xb2\x37\xd3\xd7\x61\xe5\xac\x3f\xff\x7d\xc9\x0a\x87\xc1\x57\x00\x00\x00\xff\xff\xca\x86\xd6\x11\x04\x05\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x82\x30\x10\x86\xef\x3c\xc5\x1c\x25\xe1\xb0\x77\x4f\x80\x65\x43\xd2\xb4\xbb\x5a\x12\x6f\x0d\x5b\xaa\x36\xb2\xe0\xb6\xd5\xac\x6f\xbf\x41\x44\x51\x14\x36\xea\x85\x84\xf6\xef\x74\xbe\xf9\x3b\x13\x4e\x91\xcf\x10\x30\x3f\xc0\x08\xe2\x08\x08\x65\x80\xe6\xf1\x8c\xcd\x60\xb3\x35\x2b\x5e\x94\x56\x2d\x94\x48\xad\x2a\x0b\x2e\x72\x25\x0b\xcb\x8d\xd4\x3b\xa9\x0d\x8c\x1c\x80\xcd\xf6\x2b\x57\x82\xaf\xe5\x1e\x02\x4c\x83\xc3\x79\x92\x60\xec\x39\x00\x5a\x2e\x95\xb1\x52\xcb\x0c\x02\x4a\x31\xf2\x09\x4c\x50\xe4\x27\x98\x41\xe4\xe3\x19\xba\xd4\xf0\xd4\x42\x4c\xd8\x29\xc2\x49\xfb\x56\xe9\x52\x21\xa4\x31\xdc\x96\x6b\x59\x00\x43\x73\x56\x2d\x26\x24\xfe\x4c\xd0\xe8\x9c\x83\x0b\x94\x40\x48\x49\x84\xe3\x90\xc1\x14\x7d\x60\x3f\x44\x8e\x3b\x76\x9c\x47\x38\x7f\xb6\x52\x2b\x39\xcc\x59\xeb\x3a\x00\xcd\xd6\x9e\xab\xac\x7b\xa8\x93\xbb\xd7\x68\x5f\x0b\xa1\x8a\x45\x39\x48\x50\x3b\xca\xfb\x24\xaa\x30\x36\xcd\xf3\x3a\xb6\xca\x0e\x1e\x5c\x08\x3a\x0e\x5d\xbd\x05\xab\x95\xdc\xdd\xae\x52\xb7\x16\xd7\xd7\x79\xdd\x14\x5f\x5b\x26\xab\x53\xb1\x96\x19\xff\x96\xc6\xa4\xcb\xa3\xe9\xc7\x9f\x9b\xfe\x89\x55\x6a\x6f\xd6\xa1\x89\x74\x9f\xf3\x1c\xf6\x92\x21\x7e\x27\x74\x8a\x1c\x80\x47\x21\x4c\xf5\x69\x6f\x0c\x63\x3c\x65\xf9\xe1\xbe\xff\x70\x7a\xd0\xe3\xad\xfb\x04\x71\x3d\x3e\x74\x0b\xb6\x19\x29\xf5\x5a\x17\x0a\xc0\xec\x0b\xbb\x92\x56\x89\x8a\xe9\xfe\xc4\x39\x61\xb4\xf5\x83\x8f\x2e\x26\x13\x34\x07\x95\xfd\xf2\xde\x8e\x6c\xb7\x1a\x25\xfd\xdd\xdb\xd7\x17\xee\xd8\xf9\x0b\x00\x00\xff\xff\xd9\x16\xce\x6d\xc2\x05\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1284, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6e, 0xee, 0xca, 0x95, 0x4b, 0xf0, 0x61, 0x6, 0xc, 0xad, 0x52, 0xe1, 0x81, 0x1a, 0x2d, 0xff, 0x4f, 0xb4, 0x2f, 0x6b, 0x56, 0xdf, 0x5c, 0xa7, 0xe8, 0x56, 0xaa, 0xe5, 0x6e, 0x7f, 0xb8}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1474, mode: os.FileMode(0644), modTime: time.Unix(1594797308, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3b, 0xc0, 0x6a, 0xde, 0x9a, 0xc, 0x25, 0xf3, 0x24, 0xbd, 0x44, 0xd5, 0x38, 0xd4, 0x65, 0xe5, 0x51, 0xf1, 0x2b, 0x3e, 0x84, 0x99, 0x65, 0x39, 0xb6, 0x2a, 0x79, 0x4b, 0xc5, 0x9d, 0x25, 0x75}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index afb671961..92050ee9a 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -37,4 +37,10 @@ CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( UNIQUE(message_id, public_key, installation_id) ); +CREATE TABLE IF NOT EXISTS push_notification_client_registrations ( + registration BLOB NOT NULL, + synthetic_id INT NOT NULL DEFAULT 0, + UNIQUE(synthetic_id) ON CONFLICT REPLACE +); + CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 60e265184..f3e6c3dbd 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -7,7 +7,10 @@ import ( "strings" "time" + "github.com/golang/protobuf/proto" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/protobuf" ) type Persistence struct { @@ -18,6 +21,35 @@ func NewPersistence(db *sql.DB) *Persistence { return &Persistence{db: db} } +func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotificationRegistration, error) { + var registrationBytes []byte + err := p.db.QueryRow(`SELECT registration FROM push_notification_client_registrations LIMIT 1`).Scan(®istrationBytes) + if err == sql.ErrNoRows { + return nil, nil + } else if err != nil { + return nil, err + } + + registration := &protobuf.PushNotificationRegistration{} + + err = proto.Unmarshal(registrationBytes, registration) + if err != nil { + return nil, err + } + + return registration, nil +} + +func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration) error { + marshaledRegistration, err := proto.Marshal(registration) + if err != nil { + return err + } + _, err = p.db.Exec(`INSERT INTO push_notification_client_registrations (registration) VALUES (?)`, marshaledRegistration) + return err + +} + func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) error { trackedAt := time.Now().Unix() _, err := p.db.Exec(`INSERT INTO push_notification_client_tracked_messages (chat_id, message_id, tracked_at) VALUES (?,?,?)`, chatID, messageID, trackedAt) diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index f81699240..709ce1d6b 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -5,10 +5,12 @@ import ( "os" "testing" + "github.com/golang/protobuf/proto" "github.com/stretchr/testify/suite" "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" "github.com/status-im/status-go/protocol/sqlite" ) @@ -138,3 +140,29 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { s.Require().Len(retrievedInfos, 2) } + +func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() { + // Try with nil first + retrievedRegistration, err := s.persistence.GetLastPushNotificationRegistration() + s.Require().NoError(err) + s.Require().Nil(retrievedRegistration) + + // Save & retrieve registration + registration := &protobuf.PushNotificationRegistration{ + AccessToken: "test", + Version: 3, + } + + s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration)) + retrievedRegistration, err = s.persistence.GetLastPushNotificationRegistration() + s.Require().NoError(err) + s.Require().True(proto.Equal(registration, retrievedRegistration)) + + // Override and retrieve + + registration.Version = 5 + s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration)) + retrievedRegistration, err = s.persistence.GetLastPushNotificationRegistration() + s.Require().NoError(err) + s.Require().True(proto.Equal(registration, retrievedRegistration)) +} From d2ab7f1bcdecc9bf67188de419da8fb3ba6f657c Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 10:22:43 +0200 Subject: [PATCH 29/46] refresh token only on revocation --- protocol/push_notification_client/client.go | 17 +++++-- .../migrations/migrations.go | 8 ++-- .../sql/1593601729_initial_schema.up.sql | 1 + .../push_notification_client/persistence.go | 46 +++++++++++++++---- .../persistence_test.go | 26 +++++++++-- 5 files changed, 76 insertions(+), 22 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index b2a895fa9..265a0f155 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -87,6 +87,9 @@ type Client struct { // lastPushNotificationRegistration is the latest known push notification version lastPushNotificationRegistration *protobuf.PushNotificationRegistration + // lastContactIDs is the latest contact ids array + lastContactIDs []*ecdsa.PublicKey + // AccessToken is the access token that is currently being used AccessToken string // DeviceToken is the device token for this device @@ -132,13 +135,14 @@ func (c *Client) subscribeForSentMessages() { } func (c *Client) loadLastPushNotificationRegistration() error { - lastRegistration, err := c.persistence.GetLastPushNotificationRegistration() + lastRegistration, lastContactIDs, err := c.persistence.GetLastPushNotificationRegistration() if err != nil { return err } if lastRegistration == nil { lastRegistration = &protobuf.PushNotificationRegistration{} } + c.lastContactIDs = lastContactIDs c.lastPushNotificationRegistration = lastRegistration return nil @@ -394,8 +398,13 @@ func (p *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([ return encryptedTokens, nil } -func (p *Client) getToken() string { - return uuid.New().String() +// getToken checks if we need to refresh the token +// and return a new one in that case +func (c *Client) getToken(contactIDs []*ecdsa.PublicKey) string { + if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs) { + return uuid.New().String() + } + return c.lastPushNotificationRegistration.AccessToken } func (c *Client) getVersion() uint64 { @@ -406,7 +415,7 @@ func (c *Client) getVersion() uint64 { } func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { - token := c.getToken() + token := c.getToken(contactIDs) allowedUserList, err := c.allowedUserList([]byte(token), contactIDs) if err != nil { return nil, err diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index e4c2c0cd3..6f5d47a54 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.474kB) +// 1593601729_initial_schema.up.sql (1.496kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x82\x30\x10\x86\xef\x3c\xc5\x1c\x25\xe1\xb0\x77\x4f\x80\x65\x43\xd2\xb4\xbb\x5a\x12\x6f\x0d\x5b\xaa\x36\xb2\xe0\xb6\xd5\xac\x6f\xbf\x41\x44\x51\x14\x36\xea\x85\x84\xf6\xef\x74\xbe\xf9\x3b\x13\x4e\x91\xcf\x10\x30\x3f\xc0\x08\xe2\x08\x08\x65\x80\xe6\xf1\x8c\xcd\x60\xb3\x35\x2b\x5e\x94\x56\x2d\x94\x48\xad\x2a\x0b\x2e\x72\x25\x0b\xcb\x8d\xd4\x3b\xa9\x0d\x8c\x1c\x80\xcd\xf6\x2b\x57\x82\xaf\xe5\x1e\x02\x4c\x83\xc3\x79\x92\x60\xec\x39\x00\x5a\x2e\x95\xb1\x52\xcb\x0c\x02\x4a\x31\xf2\x09\x4c\x50\xe4\x27\x98\x41\xe4\xe3\x19\xba\xd4\xf0\xd4\x42\x4c\xd8\x29\xc2\x49\xfb\x56\xe9\x52\x21\xa4\x31\xdc\x96\x6b\x59\x00\x43\x73\x56\x2d\x26\x24\xfe\x4c\xd0\xe8\x9c\x83\x0b\x94\x40\x48\x49\x84\xe3\x90\xc1\x14\x7d\x60\x3f\x44\x8e\x3b\x76\x9c\x47\x38\x7f\xb6\x52\x2b\x39\xcc\x59\xeb\x3a\x00\xcd\xd6\x9e\xab\xac\x7b\xa8\x93\xbb\xd7\x68\x5f\x0b\xa1\x8a\x45\x39\x48\x50\x3b\xca\xfb\x24\xaa\x30\x36\xcd\xf3\x3a\xb6\xca\x0e\x1e\x5c\x08\x3a\x0e\x5d\xbd\x05\xab\x95\xdc\xdd\xae\x52\xb7\x16\xd7\xd7\x79\xdd\x14\x5f\x5b\x26\xab\x53\xb1\x96\x19\xff\x96\xc6\xa4\xcb\xa3\xe9\xc7\x9f\x9b\xfe\x89\x55\x6a\x6f\xd6\xa1\x89\x74\x9f\xf3\x1c\xf6\x92\x21\x7e\x27\x74\x8a\x1c\x80\x47\x21\x4c\xf5\x69\x6f\x0c\x63\x3c\x65\xf9\xe1\xbe\xff\x70\x7a\xd0\xe3\xad\xfb\x04\x71\x3d\x3e\x74\x0b\xb6\x19\x29\xf5\x5a\x17\x0a\xc0\xec\x0b\xbb\x92\x56\x89\x8a\xe9\xfe\xc4\x39\x61\xb4\xf5\x83\x8f\x2e\x26\x13\x34\x07\x95\xfd\xf2\xde\x8e\x6c\xb7\x1a\x25\xfd\xdd\xdb\xd7\x17\xee\xd8\xf9\x0b\x00\x00\xff\xff\xd9\x16\xce\x6d\xc2\x05\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x9b\x30\x10\x86\xef\x3c\xc5\x1c\x17\x89\x43\xef\x7b\x02\xd6\x54\x48\x96\xdd\x26\x46\xca\xcd\x72\x8d\x77\x63\x85\x9a\xd4\x76\xa2\xe6\xed\x2b\x20\x24\x24\x24\x50\x25\xb9\x20\x61\xff\x1e\xcf\x37\xbf\x67\xd2\x05\x8a\x19\x02\x16\x27\x18\x41\x9e\x01\xa1\x0c\xd0\x2a\x5f\xb2\x25\x6c\x77\x6e\xcd\x4d\xed\xf5\xa7\x96\xc2\xeb\xda\x70\x59\x69\x65\x3c\x77\xca\xee\x95\x75\xf0\x16\x00\x6c\x77\xbf\x2a\x2d\xf9\x46\x1d\x20\xc1\x34\x69\xcf\x93\x02\xe3\x28\x00\xb0\xea\x4b\x3b\xaf\xac\x2a\x21\xa1\x14\xa3\x98\xc0\x07\xca\xe2\x02\x33\xc8\x62\xbc\x44\x97\x1a\x2e\x3c\xe4\x84\x9d\x22\x9c\xb4\xdf\x1a\x9d\x90\x52\x39\xc7\x7d\xbd\x51\x06\x18\x5a\xb1\x66\xb1\x20\xf9\xcf\x02\xbd\x9d\x73\x08\x81\x12\x48\x29\xc9\x70\x9e\x32\x58\xa0\x1f\x38\x4e\x51\x10\xbe\x07\xc1\x23\x9c\x7f\x76\xca\x6a\x35\xcf\xd9\xe9\x46\x00\xfd\xd6\x81\xeb\x72\x7c\x68\x94\x7b\xd4\x6b\x5f\x0b\xa1\xcd\x67\x3d\x4b\xd0\x39\xca\xa7\x24\xda\x38\x2f\xaa\xaa\x8b\xad\xcb\xd6\x83\x0b\xc1\xc8\xa1\xab\xb7\xe0\xad\x56\xfb\xdb\x55\x1a\xd7\xe2\xfa\xba\x68\x9c\xe2\x6b\xcb\xe4\xad\x90\x1b\x55\xf2\xdf\xca\x39\xf1\x75\x34\xfd\xf8\x73\xd3\x3f\xb9\x16\xfe\x66\x1d\xfa\x48\xf7\x39\xcf\x61\x2f\x19\xf2\xef\x84\x2e\x50\x00\xf0\x28\x84\x6b\x3e\xc3\x8d\x79\x8c\xa7\x2c\x6f\xef\xfb\x1f\xce\x08\x26\xbc\x0d\x9f\x20\xee\xc6\x87\x1d\xc0\xf6\x23\xa5\x5b\x1b\x43\x01\xc8\xda\x78\x21\x1b\xf3\x5c\xbb\xdd\xad\xba\x83\xf1\x6b\xe5\xb5\x6c\x48\xef\xcf\xa1\x13\xdc\x50\x3f\xfb\x14\x73\xf2\x81\x56\xa0\xcb\xbf\x7c\xb2\x4f\x87\x0d\x48\xc9\x74\x4f\x4f\x75\x4b\xf8\x1e\xfc\x0b\x00\x00\xff\xff\x1c\x18\x75\x11\xd8\x05\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1474, mode: os.FileMode(0644), modTime: time.Unix(1594797308, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3b, 0xc0, 0x6a, 0xde, 0x9a, 0xc, 0x25, 0xf3, 0x24, 0xbd, 0x44, 0xd5, 0x38, 0xd4, 0x65, 0xe5, 0x51, 0xf1, 0x2b, 0x3e, 0x84, 0x99, 0x65, 0x39, 0xb6, 0x2a, 0x79, 0x4b, 0xc5, 0x9d, 0x25, 0x75}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1496, mode: os.FileMode(0644), modTime: time.Unix(1594800672, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0xb6, 0xaf, 0x11, 0x19, 0xf8, 0xc6, 0xed, 0x2f, 0xf5, 0x42, 0x54, 0xdd, 0x65, 0xf7, 0x39, 0xbc, 0x19, 0xff, 0x72, 0xa1, 0x38, 0x87, 0xfa, 0x6d, 0xd5, 0xe3, 0x6a, 0x49, 0x65, 0x3c, 0x49}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index 92050ee9a..c4c6b6999 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -39,6 +39,7 @@ CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( CREATE TABLE IF NOT EXISTS push_notification_client_registrations ( registration BLOB NOT NULL, + contact_ids BLOB, synthetic_id INT NOT NULL DEFAULT 0, UNIQUE(synthetic_id) ON CONFLICT REPLACE ); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index f3e6c3dbd..4afe0f815 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -1,9 +1,11 @@ package push_notification_client import ( + "bytes" "context" "crypto/ecdsa" "database/sql" + "encoding/gob" "strings" "time" @@ -21,33 +23,59 @@ func NewPersistence(db *sql.DB) *Persistence { return &Persistence{db: db} } -func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotificationRegistration, error) { +func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotificationRegistration, []*ecdsa.PublicKey, error) { var registrationBytes []byte - err := p.db.QueryRow(`SELECT registration FROM push_notification_client_registrations LIMIT 1`).Scan(®istrationBytes) + var contactIDsBytes []byte + err := p.db.QueryRow(`SELECT registration,contact_ids FROM push_notification_client_registrations LIMIT 1`).Scan(®istrationBytes, &contactIDsBytes) if err == sql.ErrNoRows { - return nil, nil + return nil, nil, nil } else if err != nil { - return nil, err + return nil, nil, err + } + + var publicKeyBytes [][]byte + var contactIDs []*ecdsa.PublicKey + // Restore contactIDs + contactIDsDecoder := gob.NewDecoder(bytes.NewBuffer(contactIDsBytes)) + err = contactIDsDecoder.Decode(&publicKeyBytes) + if err != nil { + return nil, nil, err + } + for _, pkBytes := range publicKeyBytes { + pk, err := crypto.UnmarshalPubkey(pkBytes) + if err != nil { + return nil, nil, err + } + contactIDs = append(contactIDs, pk) } registration := &protobuf.PushNotificationRegistration{} err = proto.Unmarshal(registrationBytes, registration) if err != nil { - return nil, err + return nil, nil, err } - return registration, nil + return registration, contactIDs, nil } -func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration) error { +func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error { + var encodedContactIDs bytes.Buffer + var contactIDsBytes [][]byte + for _, pk := range contactIDs { + contactIDsBytes = append(contactIDsBytes, crypto.FromECDSAPub(pk)) + } + pkEncoder := gob.NewEncoder(&encodedContactIDs) + if err := pkEncoder.Encode(contactIDsBytes); err != nil { + return err + } + marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - _, err = p.db.Exec(`INSERT INTO push_notification_client_registrations (registration) VALUES (?)`, marshaledRegistration) + _, err = p.db.Exec(`INSERT INTO push_notification_client_registrations (registration,contact_ids) VALUES (?, ?)`, marshaledRegistration, encodedContactIDs.Bytes()) return err - } func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) error { diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 709ce1d6b..0cf2947c3 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -1,6 +1,7 @@ package push_notification_client import ( + "crypto/ecdsa" "io/ioutil" "os" "testing" @@ -143,9 +144,10 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() { // Try with nil first - retrievedRegistration, err := s.persistence.GetLastPushNotificationRegistration() + retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration() s.Require().NoError(err) s.Require().Nil(retrievedRegistration) + s.Require().Nil(retrievedContactIDs) // Save & retrieve registration registration := &protobuf.PushNotificationRegistration{ @@ -153,16 +155,30 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() { Version: 3, } - s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration)) - retrievedRegistration, err = s.persistence.GetLastPushNotificationRegistration() + key1, err := crypto.GenerateKey() + s.Require().NoError(err) + + key2, err := crypto.GenerateKey() + s.Require().NoError(err) + + key3, err := crypto.GenerateKey() + s.Require().NoError(err) + + publicKeys := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} + + s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys)) + retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration() s.Require().NoError(err) s.Require().True(proto.Equal(registration, retrievedRegistration)) + s.Require().Equal(publicKeys, retrievedContactIDs) // Override and retrieve registration.Version = 5 - s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration)) - retrievedRegistration, err = s.persistence.GetLastPushNotificationRegistration() + publicKeys = append(publicKeys, &key3.PublicKey) + s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys)) + retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration() s.Require().NoError(err) s.Require().True(proto.Equal(registration, retrievedRegistration)) + s.Require().Equal(publicKeys, retrievedContactIDs) } From 8130e5b402a26bd06a96cab7f96495c331ee5352 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 12:29:16 +0200 Subject: [PATCH 30/46] Run registration loop when servers are not fully registered --- protocol/push_notification_client/client.go | 113 ++++++++++++++++-- .../migrations/migrations.go | 8 +- .../sql/1593601729_initial_schema.up.sql | 2 + .../push_notification_client/persistence.go | 6 +- 4 files changed, 113 insertions(+), 16 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 265a0f155..abef9e148 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -11,6 +11,7 @@ import ( "encoding/json" "errors" "io" + "math" "sort" "time" @@ -29,11 +30,19 @@ const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 +// maxRetries is the maximum number of attempts we do before giving up registering with a server +const maxRetries int64 = 12 + +// RegistrationBackoffTime is the step of the exponential backoff +const RegistrationBackoffTime int64 = 15 + type PushNotificationServer struct { - PublicKey *ecdsa.PublicKey `json:"-"` - Registered bool `json:"registered,omitempty"` - RegisteredAt int64 `json:"registeredAt,omitempty"` - AccessToken string `json:"accessToken,omitempty"` + PublicKey *ecdsa.PublicKey `json:"-"` + Registered bool `json:"registered,omitempty"` + RegisteredAt int64 `json:"registeredAt,omitempty"` + LastRetriedAt int64 `json:"lastRetriedAt,omitempty"` + RetryCount int64 `json:"retryCount,omitempty"` + AccessToken string `json:"accessToken,omitempty"` } func (s *PushNotificationServer) MarshalJSON() ([]byte, error) { @@ -68,8 +77,7 @@ type Config struct { // AllowOnlyFromContacts indicates whether we should be receiving push notifications // only from contacts AllowOnlyFromContacts bool - // PushNotificationServers is an array of push notification servers we want to register with - PushNotificationServers []*PushNotificationServer + // InstallationID is the installation-id for this device InstallationID string @@ -99,8 +107,10 @@ type Client struct { reader io.Reader //messageProcessor is a message processor used to send and being notified of messages - messageProcessor *common.MessageProcessor + + // registrationLoopQuitChan is a channel to indicate to the registration loop that should be terminating + registrationLoopQuitChan chan struct{} } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { @@ -109,7 +119,8 @@ func New(persistence *Persistence, config *Config, processor *common.MessageProc config: config, messageProcessor: processor, persistence: persistence, - reader: rand.Reader} + reader: rand.Reader, + } } func (c *Client) subscribeForSentMessages() { @@ -147,6 +158,19 @@ func (c *Client) loadLastPushNotificationRegistration() error { return nil } +func (c *Client) stopRegistrationLoop() { + // stop old registration loop + if c.registrationLoopQuitChan != nil { + close(c.registrationLoopQuitChan) + c.registrationLoopQuitChan = nil + } +} + +func (c *Client) startRegistrationLoop() { + c.stopRegistrationLoop() + c.registrationLoopQuitChan = make(chan struct{}) + go c.registrationLoop() +} func (c *Client) Start() error { if c.messageProcessor == nil { @@ -158,12 +182,16 @@ func (c *Client) Start() error { return err } c.subscribeForSentMessages() + c.startRegistrationLoop() return nil } func (c *Client) Stop() error { close(c.quit) + if c.registrationLoopQuitChan != nil { + close(c.registrationLoopQuitChan) + } return nil } @@ -450,10 +478,21 @@ func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.Public return false } +func nextServerRetry(server *PushNotificationServer) int64 { + return server.LastRetriedAt + RegistrationBackoffTime*server.RetryCount*int64(math.Exp2(float64(server.RetryCount))) +} + +// We calculate if it's too early to retry, by exponentially backing off +func shouldRetryRegisteringWithServer(server *PushNotificationServer) bool { + return time.Now().Unix() < nextServerRetry(server) +} + func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegistration, server *PushNotificationServer) error { // Reset server registration data server.Registered = false server.RegisteredAt = 0 + server.RetryCount += 1 + server.LastRetriedAt = time.Now().Unix() server.AccessToken = registration.AccessToken if err := c.persistence.UpsertServer(server); err != nil { @@ -489,10 +528,64 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis return err } return nil +} +func (c *Client) registrationLoop() error { + for { + c.config.Logger.Info("runing registration loop") + servers, err := c.persistence.GetServers() + if err != nil { + c.config.Logger.Error("failed retrieving servers, quitting registration loop", zap.Error(err)) + return err + } + if len(servers) == 0 { + c.config.Logger.Debug("nothing to do, quitting registration loop") + return nil + } + + var nonRegisteredServers []*PushNotificationServer + for _, server := range servers { + if server.Registered { + nonRegisteredServers = append(nonRegisteredServers, server) + } + if len(nonRegisteredServers) == 0 { + c.config.Logger.Debug("registered with all servers, quitting registration loop") + return nil + } + + var lowestNextRetry int64 + + for _, server := range nonRegisteredServers { + if shouldRetryRegisteringWithServer(server) { + err := c.registerWithServer(c.lastPushNotificationRegistration, server) + if err != nil { + return err + } + } + nextRetry := nextServerRetry(server) + if lowestNextRetry == 0 || nextRetry < lowestNextRetry { + lowestNextRetry = nextRetry + } + + } + + nextRetry := lowestNextRetry - time.Now().Unix() + waitFor := time.Duration(nextRetry) + select { + + case <-time.After(waitFor * time.Second): + case <-c.registrationLoopQuitChan: + return nil + + } + } + } } func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]*PushNotificationServer, error) { + // stop registration loop + c.stopRegistrationLoop() + c.DeviceToken = deviceToken servers, err := c.persistence.GetServers() if err != nil { @@ -526,7 +619,9 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut case <-c.quit: return servers, nil case <-ctx.Done(): - c.config.Logger.Info("Context done") + c.config.Logger.Info("could not register all servers") + // start registration loop + c.startRegistrationLoop() return servers, nil case <-time.After(200 * time.Millisecond): servers, err = c.persistence.GetServersByPublicKey(serverPublicKeys) diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index 6f5d47a54..d6c7865d8 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.496kB) +// 1593601729_initial_schema.up.sql (1.576kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x9b\x30\x10\x86\xef\x3c\xc5\x1c\x17\x89\x43\xef\x7b\x02\xd6\x54\x48\x96\xdd\x26\x46\xca\xcd\x72\x8d\x77\x63\x85\x9a\xd4\x76\xa2\xe6\xed\x2b\x20\x24\x24\x24\x50\x25\xb9\x20\x61\xff\x1e\xcf\x37\xbf\x67\xd2\x05\x8a\x19\x02\x16\x27\x18\x41\x9e\x01\xa1\x0c\xd0\x2a\x5f\xb2\x25\x6c\x77\x6e\xcd\x4d\xed\xf5\xa7\x96\xc2\xeb\xda\x70\x59\x69\x65\x3c\x77\xca\xee\x95\x75\xf0\x16\x00\x6c\x77\xbf\x2a\x2d\xf9\x46\x1d\x20\xc1\x34\x69\xcf\x93\x02\xe3\x28\x00\xb0\xea\x4b\x3b\xaf\xac\x2a\x21\xa1\x14\xa3\x98\xc0\x07\xca\xe2\x02\x33\xc8\x62\xbc\x44\x97\x1a\x2e\x3c\xe4\x84\x9d\x22\x9c\xb4\xdf\x1a\x9d\x90\x52\x39\xc7\x7d\xbd\x51\x06\x18\x5a\xb1\x66\xb1\x20\xf9\xcf\x02\xbd\x9d\x73\x08\x81\x12\x48\x29\xc9\x70\x9e\x32\x58\xa0\x1f\x38\x4e\x51\x10\xbe\x07\xc1\x23\x9c\x7f\x76\xca\x6a\x35\xcf\xd9\xe9\x46\x00\xfd\xd6\x81\xeb\x72\x7c\x68\x94\x7b\xd4\x6b\x5f\x0b\xa1\xcd\x67\x3d\x4b\xd0\x39\xca\xa7\x24\xda\x38\x2f\xaa\xaa\x8b\xad\xcb\xd6\x83\x0b\xc1\xc8\xa1\xab\xb7\xe0\xad\x56\xfb\xdb\x55\x1a\xd7\xe2\xfa\xba\x68\x9c\xe2\x6b\xcb\xe4\xad\x90\x1b\x55\xf2\xdf\xca\x39\xf1\x75\x34\xfd\xf8\x73\xd3\x3f\xb9\x16\xfe\x66\x1d\xfa\x48\xf7\x39\xcf\x61\x2f\x19\xf2\xef\x84\x2e\x50\x00\xf0\x28\x84\x6b\x3e\xc3\x8d\x79\x8c\xa7\x2c\x6f\xef\xfb\x1f\xce\x08\x26\xbc\x0d\x9f\x20\xee\xc6\x87\x1d\xc0\xf6\x23\xa5\x5b\x1b\x43\x01\xc8\xda\x78\x21\x1b\xf3\x5c\xbb\xdd\xad\xba\x83\xf1\x6b\xe5\xb5\x6c\x48\xef\xcf\xa1\x13\xdc\x50\x3f\xfb\x14\x73\xf2\x81\x56\xa0\xcb\xbf\x7c\xb2\x4f\x87\x0d\x48\xc9\x74\x4f\x4f\x75\x4b\xf8\x1e\xfc\x0b\x00\x00\xff\xff\x1c\x18\x75\x11\xd8\x05\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x9b\x30\x10\x86\xef\x3c\xc5\x1c\x17\x89\x43\xef\x7b\x02\xd6\x54\x48\x96\xdd\x26\x46\xca\xcd\x72\x8d\x77\x63\x85\x9a\xad\x6d\x56\xe5\xed\x2b\x60\xc9\x92\x90\x40\x95\xe4\x82\x84\xfd\x7b\xec\x6f\xfe\x99\x49\x37\x28\x66\x08\x58\x9c\x60\x04\x79\x06\x84\x32\x40\xbb\x7c\xcb\xb6\xf0\xde\xb8\x3d\x37\xb5\xd7\xaf\x5a\x0a\xaf\x6b\xc3\x65\xa5\x95\xf1\xdc\x29\xfb\xa1\xac\x83\xa7\x00\xe0\xbd\xf9\x55\x69\xc9\x0f\xaa\x85\x04\xd3\xa4\x3f\x4f\x0a\x8c\xa3\x00\xc0\xaa\x37\xed\xbc\xb2\xaa\x84\x84\x52\x8c\x62\x02\x2f\x28\x8b\x0b\xcc\x20\x8b\xf1\x16\x9d\x6a\xb8\xf0\x90\x13\x76\x8c\x70\xd4\x7e\xeb\x74\x95\x70\x9e\x5b\xe5\xad\x5e\x53\x76\xa2\x96\xcb\xba\x31\x4b\x2a\x21\xa5\x72\x8e\xfb\xfa\xa0\x0c\x30\xb4\x63\xdd\x62\x41\xf2\x9f\x05\x7a\xfa\x62\x0a\x81\x12\x48\x29\xc9\x70\x9e\x32\xd8\xa0\x1f\x38\x4e\x51\x10\x3e\x07\xc1\x2d\x79\xfb\xd3\x28\xab\xd5\x7a\xde\x06\xdd\x0c\x73\xdc\x6a\xb9\x2e\xe7\x87\x66\x6f\x8f\x46\xed\x63\x21\xb4\x79\xad\x57\x09\x86\x0a\xe1\x4b\x12\x6d\x9c\x17\x55\x35\xc4\xd6\x65\xef\xc1\x89\x60\xe6\xd0\x59\x6d\x75\xa5\xf0\x71\x39\x4b\xf3\x5c\x9c\x5f\x17\xcd\x9f\xf8\xd8\x34\x79\x2b\xe4\x41\x95\xfc\xb7\x72\x4e\xbc\x7d\x9a\xfe\xf9\x73\xd1\x3f\xb9\x17\xfe\x62\x1e\xc6\x48\xd7\x39\xbf\xc2\x9e\x32\xe4\xdf\x09\xdd\xa0\x00\xe0\x56\x08\xd7\x7d\xa6\x1b\xeb\x18\x77\x59\xde\xdf\xf7\x3f\x9c\x11\x2c\x78\x1b\xde\x41\x3c\x8c\x23\x3b\x81\x1d\x47\xd4\xb0\x36\x87\x02\x90\xb5\xf1\x42\x76\xe6\xb9\x7e\x7b\x58\x75\xad\xf1\x7b\xe5\xb5\xec\x48\xaf\xcf\xa1\x23\xdc\x54\xbf\x5a\x8a\x39\x79\x41\x3b\xd0\xe5\x5f\xbe\xd8\xa7\xd3\x06\xa4\x64\xb9\xa7\x97\xba\x25\x7c\x0e\xfe\x05\x00\x00\xff\xff\x58\xe5\xb5\x4b\x28\x06\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1496, mode: os.FileMode(0644), modTime: time.Unix(1594800672, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0xb6, 0xaf, 0x11, 0x19, 0xf8, 0xc6, 0xed, 0x2f, 0xf5, 0x42, 0x54, 0xdd, 0x65, 0xf7, 0x39, 0xbc, 0x19, 0xff, 0x72, 0xa1, 0x38, 0x87, 0xfa, 0x6d, 0xd5, 0xe3, 0x6a, 0x49, 0x65, 0x3c, 0x49}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1576, mode: os.FileMode(0644), modTime: time.Unix(1594803635, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xe, 0xc, 0xdf, 0xb6, 0xdb, 0x22, 0x1, 0x1f, 0xe0, 0x18, 0x96, 0x1b, 0x13, 0x5f, 0x60, 0x2c, 0x41, 0xd5, 0x4, 0x77, 0x99, 0x8a, 0x4d, 0xa1, 0xd6, 0x41, 0xb5, 0x1, 0x86, 0x29, 0x1e}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index c4c6b6999..86be44f03 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -2,6 +2,8 @@ CREATE TABLE IF NOT EXISTS push_notification_client_servers ( public_key BLOB NOT NULL, registered BOOLEAN DEFAULT FALSE, registered_at INT NOT NULL DEFAULT 0, + last_retried_at INT NOT NULL DEFAULT 0, + retry_count INT NOT NULL DEFAULT 0, access_token TEXT, UNIQUE(public_key) ON CONFLICT REPLACE ); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 4afe0f815..10a800f95 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -261,13 +261,13 @@ func (p *Persistence) NotifiedOn(publicKey *ecdsa.PublicKey, installationID stri } func (p *Persistence) UpsertServer(server *PushNotificationServer) error { - _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token) VALUES (?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken) + _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token, last_retried_at, retry_count) VALUES (?,?,?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken, server.LastRetriedAt, server.RetryCount) return err } func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { - rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token FROM push_notification_client_servers`) + rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token,last_retried_at, retry_count FROM push_notification_client_servers`) if err != nil { return nil, err } @@ -275,7 +275,7 @@ func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { for rows.Next() { server := &PushNotificationServer{} var key []byte - err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken) + err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken, &server.LastRetriedAt, &server.RetryCount) if err != nil { return nil, err } From be1800f2ec2b060d3df06e4942f5b3e5b4a6b254 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 14:25:01 +0200 Subject: [PATCH 31/46] drive settings from client --- appdatabase/migrations/bindata.go | 50 ++++++--- .../sql/0008_add_push_notifications.up.sql | 3 + multiaccounts/accounts/database.go | 102 +++++++++++------- protocol/messenger.go | 19 ++-- protocol/messenger_config.go | 9 ++ protocol/migrations/migrations.go | 4 +- services/ext/service.go | 25 ++++- 7 files changed, 144 insertions(+), 68 deletions(-) create mode 100644 appdatabase/migrations/sql/0008_add_push_notifications.up.sql diff --git a/appdatabase/migrations/bindata.go b/appdatabase/migrations/bindata.go index 6b18606ae..e2294942b 100644 --- a/appdatabase/migrations/bindata.go +++ b/appdatabase/migrations/bindata.go @@ -12,6 +12,7 @@ // 0005_waku_mode.up.sql (146B) // 0006_appearance.up.sql (67B) // 0007_enable_waku_default.up.sql (38B) +// 0008_add_push_notifications.up.sql (214B) // doc.go (74B) package migrations @@ -321,6 +322,26 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) { return a, nil } +var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x4e\x2d\x29\xc9\xcc\x4b\x2f\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x4a\xcd\xcd\x2f\x49\x8d\x2f\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x8e\x4f\xcd\x4b\x4c\xca\x49\x4d\x51\x70\xf2\xf7\xf7\x71\x75\xf4\xb3\xe6\x22\x64\x54\x71\x6a\x5e\x0a\x16\x83\x88\x37\x00\x43\x6f\x7c\x71\x6a\x51\x59\x6a\x11\xa6\x53\x00\x01\x00\x00\xff\xff\xfc\xde\xba\xf0\xd6\x00\x00\x00") + +func _0008_add_push_notificationsUpSqlBytes() ([]byte, error) { + return bindataRead( + __0008_add_push_notificationsUpSql, + "0008_add_push_notifications.up.sql", + ) +} + +func _0008_add_push_notificationsUpSql() (*asset, error) { + bytes, err := _0008_add_push_notificationsUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 214, mode: os.FileMode(0644), modTime: time.Unix(1594815858, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0x77, 0x87, 0xd3, 0xd7, 0x50, 0x58, 0x80, 0x8f, 0x56, 0x4c, 0x2f, 0x1b, 0x58, 0x4d, 0x98, 0xdd, 0x3, 0xc5, 0x91, 0xad, 0x5e, 0xfe, 0xb, 0x54, 0xb3, 0x7e, 0xd8, 0xf7, 0x25, 0xa0, 0xda}} + return a, nil +} + var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00") func docGoBytes() ([]byte, error) { @@ -456,6 +477,8 @@ var _bindata = map[string]func() (*asset, error){ "0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql, + "0008_add_push_notifications.up.sql": _0008_add_push_notificationsUpSql, + "doc.go": docGo, } @@ -500,19 +523,20 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}}, - "0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}}, - "0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}}, - "0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}}, - "0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}}, - "0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}}, - "0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}}, - "0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}}, - "0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}}, - "0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}}, - "0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}}, - "0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, + "0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}}, + "0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}}, + "0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}}, + "0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}}, + "0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}}, + "0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}}, + "0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}}, + "0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}}, + "0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}}, + "0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}}, + "0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}}, + "0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}}, + "0008_add_push_notifications.up.sql": &bintree{_0008_add_push_notificationsUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/appdatabase/migrations/sql/0008_add_push_notifications.up.sql b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql new file mode 100644 index 000000000..2add23e03 --- /dev/null +++ b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN; +ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN; +ALTER TABLE settings ADD COLUMN push_notification_server_enabled BOOLEAN; diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index ce142d5c2..09a79d1d1 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -38,46 +38,49 @@ type Account struct { type Settings struct { // required - Address types.Address `json:"address"` - ChaosMode bool `json:"chaos-mode?,omitempty"` - Currency string `json:"currency,omitempty"` - CurrentNetwork string `json:"networks/current-network"` - CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` - CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` - DappsAddress types.Address `json:"dapps-address"` - EIP1581Address types.Address `json:"eip1581-address"` - Fleet *string `json:"fleet,omitempty"` - HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` - InstallationID string `json:"installation-id"` - KeyUID string `json:"key-uid"` - KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` - KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` - KeycardPairing string `json:"keycard-pairing,omitempty"` - LastUpdated *int64 `json:"last-updated,omitempty"` - LatestDerivedPath uint `json:"latest-derived-path"` - LogLevel *string `json:"log-level,omitempty"` - Mnemonic *string `json:"mnemonic,omitempty"` - Name string `json:"name,omitempty"` - Networks *json.RawMessage `json:"networks/networks"` - NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` - PhotoPath string `json:"photo-path"` - PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` - PreferredName *string `json:"preferred-name,omitempty"` - PreviewPrivacy bool `json:"preview-privacy?"` - PublicKey string `json:"public-key"` - RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` - SigningPhrase string `json:"signing-phrase"` - StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` - StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` - StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` - SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` - Appearance uint `json:"appearance"` - Usernames *json.RawMessage `json:"usernames,omitempty"` - WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` - WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` - WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` - WakuEnabled bool `json:"waku-enabled,omitempty"` - WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` + Address types.Address `json:"address"` + ChaosMode bool `json:"chaos-mode?,omitempty"` + Currency string `json:"currency,omitempty"` + CurrentNetwork string `json:"networks/current-network"` + CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` + CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` + DappsAddress types.Address `json:"dapps-address"` + EIP1581Address types.Address `json:"eip1581-address"` + Fleet *string `json:"fleet,omitempty"` + HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` + InstallationID string `json:"installation-id"` + KeyUID string `json:"key-uid"` + KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` + KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` + KeycardPairing string `json:"keycard-pairing,omitempty"` + LastUpdated *int64 `json:"last-updated,omitempty"` + LatestDerivedPath uint `json:"latest-derived-path"` + LogLevel *string `json:"log-level,omitempty"` + Mnemonic *string `json:"mnemonic,omitempty"` + Name string `json:"name,omitempty"` + Networks *json.RawMessage `json:"networks/networks"` + NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` + PhotoPath string `json:"photo-path"` + PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` + PreferredName *string `json:"preferred-name,omitempty"` + PreviewPrivacy bool `json:"preview-privacy?"` + PublicKey string `json:"public-key"` + PushNotificationServerEnabled bool `json:"push-notification-server-enabled,omitempty"` + RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` + RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled,omitempty"` + SigningPhrase string `json:"signing-phrase"` + StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` + StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` + StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` + SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` + SendPushNotifications bool `json:"send-push-notifications,omitempty"` + Appearance uint `json:"appearance"` + Usernames *json.RawMessage `json:"usernames,omitempty"` + WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` + WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` + WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` + WakuEnabled bool `json:"waku-enabled,omitempty"` + WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` } func NewDB(db *sql.DB) *Database { @@ -246,6 +249,25 @@ func (db *Database) SaveSetting(setting string, value interface{}) error { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET remember_syncing_choice = ? WHERE synthetic_id = 'id'") + case "remote-push-notifications-enabled": + _, ok := value.(bool) + if !ok { + return ErrInvalidConfig + } + update, err = db.db.Prepare("UPDATE settings SET remote_push_notifications_enabled = ? WHERE synthetic_id = 'id'") + case "push-notifications-server-enabled": + _, ok := value.(bool) + if !ok { + return ErrInvalidConfig + } + update, err = db.db.Prepare("UPDATE settings SET push_notification_server_enabled = ? WHERE synthetic_id = 'id'") + + case "send-push-notifications": + _, ok := value.(bool) + if !ok { + return ErrInvalidConfig + } + update, err = db.db.Prepare("UPDATE settings SET send_push_notifications = ? WHERE synthetic_id = 'id'") case "stickers/packs-installed": value = &sqlite.JSONBlob{value} update, err = db.db.Prepare("UPDATE settings SET stickers_packs_installed = ? WHERE synthetic_id = 'id'") diff --git a/protocol/messenger.go b/protocol/messenger.go index fea068707..8e7d76e90 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -256,15 +256,18 @@ func NewMessenger( } pushNotificationClientPersistence := push_notification_client.NewPersistence(database) - // Hardcoding for now - pushNotificationClientConfig := &push_notification_client.Config{ - Identity: identity, - TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, - SendEnabled: true, - Logger: logger, - RemoteNotificationsEnabled: true, - InstallationID: installationID, + pushNotificationClientConfig := c.pushNotificationClientConfig + if pushNotificationClientConfig == nil { + pushNotificationClientConfig = &push_notification_client.Config{} } + + // Overriding until we handle sending/receiving from multiple identities + pushNotificationClientConfig.Identity = identity + // Hardcoding this for now, as it's the only one we support + pushNotificationClientConfig.TokenType = protobuf.PushNotificationRegistration_APN_TOKEN + pushNotificationClientConfig.Logger = logger + pushNotificationClientConfig.InstallationID = installationID + pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor) handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database}) diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index 253182277..72573dec9 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -5,6 +5,7 @@ import ( "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/push_notification_client" "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/transport" "go.uber.org/zap" @@ -34,6 +35,7 @@ type config struct { verifyTransactionClient EthClient pushNotificationServerConfig *push_notification_server.Config + pushNotificationClientConfig *push_notification_client.Config logger *zap.Logger } @@ -98,6 +100,13 @@ func WithPushNotificationServerConfig(pushNotificationServerConfig *push_notific } } +func WithPushNotificationClientConfig(pushNotificationClientConfig *push_notification_client.Config) Option { + return func(c *config) error { + c.pushNotificationClientConfig = pushNotificationClientConfig + return nil + } +} + func WithDatasync() func(c *config) error { return func(c *config) error { c.featureFlags.Datasync = true diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index 059166b16..a668f1cf7 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -338,7 +338,7 @@ func _1593087212_add_mute_chatDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1593087310, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1594393604, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -358,7 +358,7 @@ func _1593087212_add_mute_chatUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1593091669, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1594393604, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe, 0x9, 0xa0, 0x4a, 0x8e, 0x23, 0xe4, 0xce, 0xbc, 0xd4, 0x9, 0xeb, 0xf9, 0x67, 0x90, 0xc0, 0x4b, 0x67, 0x84, 0xe4, 0x42, 0x8d, 0x0, 0x17, 0x29, 0x7f, 0x12, 0xbf, 0x7d, 0x4e, 0x78, 0xec}} return a, nil } diff --git a/services/ext/service.go b/services/ext/service.go index 2e5e9669c..54b0fd80d 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -33,6 +33,7 @@ import ( coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol" + "github.com/status-im/status-go/protocol/push_notification_client" "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/transport" ) @@ -145,7 +146,12 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z EnvelopeEventsHandler: EnvelopeSignalHandler{}, Logger: logger, } - options := buildMessengerOptions(s.config, identity, db, envelopesMonitorConfig, logger) + s.accountsDB = accounts.NewDB(db) + + options, err := buildMessengerOptions(s.config, identity, db, envelopesMonitorConfig, s.accountsDB, logger) + if err != nil { + return err + } messenger, err := protocol.NewMessenger( identity, @@ -156,7 +162,6 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z if err != nil { return err } - s.accountsDB = accounts.NewDB(db) s.messenger = messenger return messenger.Init() } @@ -446,8 +451,9 @@ func buildMessengerOptions( identity *ecdsa.PrivateKey, db *sql.DB, envelopesMonitorConfig *transport.EnvelopesMonitorConfig, + accountsDB *accounts.Database, logger *zap.Logger, -) []protocol.Option { +) ([]protocol.Option, error) { options := []protocol.Option{ protocol.WithCustomLogger(logger), protocol.WithDatabase(db), @@ -458,14 +464,23 @@ func buildMessengerOptions( if config.DataSyncEnabled { options = append(options, protocol.WithDatasync()) } + settings, err := accountsDB.GetSettings() + if err != nil { + return nil, err + } - if config.PushNotificationServerEnabled { + if config.PushNotificationServerEnabled || settings.PushNotificationServerEnabled { config := &push_notification_server.Config{ Logger: logger, } options = append(options, protocol.WithPushNotificationServerConfig(config)) } + options = append(options, protocol.WithPushNotificationClientConfig(&push_notification_client.Config{ + SendEnabled: settings.SendPushNotifications, + RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled, + })) + if config.VerifyTransactionURL != "" { client := &verifyTransactionClient{ url: config.VerifyTransactionURL, @@ -474,5 +489,5 @@ func buildMessengerOptions( options = append(options, protocol.WithVerifyTransactionClient(client)) } - return options + return options, nil } From 7bad800cb87438ac2b0127aedadf63d0e2846587 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 15 Jul 2020 14:43:15 +0200 Subject: [PATCH 32/46] Add endpoints to enable/disable pns --- params/config.go | 3 -- protocol/messenger.go | 20 +++++++++++++ protocol/push_notification_client/client.go | 24 +++++++++++----- services/ext/api.go | 31 +++++++++++++++++++++ services/ext/service.go | 2 +- 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/params/config.go b/params/config.go index 3ad56d79d..54b6ba923 100644 --- a/params/config.go +++ b/params/config.go @@ -523,9 +523,6 @@ type ShhextConfig struct { VerifyENSContractAddress string VerifyTransactionChainID int64 - - // PushNotificationServerEnabled indicates whether a push notification server should be started - PushNotificationServerEnabled bool } // Validate validates the ShhextConfig struct and returns an error if inconsistent values are found diff --git a/protocol/messenger.go b/protocol/messenger.go index 8e7d76e90..39f6b2a73 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3043,6 +3043,26 @@ func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ec return m.pushNotificationClient.AddPushNotificationServer(publicKey) } +func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error { + return nil +} + +func (m *Messenger) DisableSendingPushNotifications() error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + m.pushNotificationClient.DisableSending() + return nil +} + +func (m *Messenger) EnableSendingPushNotifications() error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + m.pushNotificationClient.EnableSending() + return nil +} + // RegisterForPushNotification register deviceToken with any push notification server enabled func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { if m.pushNotificationClient == nil { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index abef9e148..5f13c30b6 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -195,12 +195,6 @@ func (c *Client) Stop() error { return nil } -// The message has been sent -// We should: -// 1) Check whether we should notify on anything -// 2) Refresh info if necessaary -// 3) Sent push notifications -// TODO: handle DH messages func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { c.config.Logger.Info("sent message", zap.Any("sent message", sentMessage)) if !c.config.SendEnabled { @@ -775,9 +769,17 @@ func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { } } - return c.persistence.UpsertServer(&PushNotificationServer{ + err = c.persistence.UpsertServer(&PushNotificationServer{ PublicKey: publicKey, }) + if err != nil { + return err + } + + if c.config.RemoteNotificationsEnabled { + c.startRegistrationLoop() + } + return nil } func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { @@ -814,6 +816,14 @@ func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installatio } } +func (c *Client) EnableSending() { + c.config.SendEnabled = true +} + +func (c *Client) DisableSending() { + c.config.SendEnabled = false +} + func (c *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { encodedPublicKey := hex.EncodeToString(hashedPublicKey) return c.messageProcessor.JoinPublic(encodedPublicKey) diff --git a/services/ext/api.go b/services/ext/api.go index 11968a44d..468f01dab 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -406,19 +406,50 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error { // PushNotifications server func (api *PublicAPI) StartPushNotificationServer() error { + // update settings return api.service.messenger.StartPushNotificationServer() } func (api *PublicAPI) StopPushNotificationServer() error { + // update settings return api.service.messenger.StopPushNotificationServer() } // PushNotification client func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { + err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled", true) + if err != nil { + return nil, err + } return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken) } +func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled", false) + if err != nil { + return err + } + return api.service.messenger.UnregisterFromPushNotifications(ctx) +} + +func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("send-push-notifications", false) + if err != nil { + return err + } + + return api.service.messenger.DisableSendingPushNotifications() +} + +func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("send-push-notifications", true) + if err != nil { + return err + } + return api.service.messenger.EnableSendingPushNotifications() +} + func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) if err != nil { diff --git a/services/ext/service.go b/services/ext/service.go index 54b0fd80d..d08f0e8ee 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -469,7 +469,7 @@ func buildMessengerOptions( return nil, err } - if config.PushNotificationServerEnabled || settings.PushNotificationServerEnabled { + if settings.PushNotificationServerEnabled { config := &push_notification_server.Config{ Logger: logger, } From 7a54d9b1c96508431be5687c4439985c046d5909 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 16 Jul 2020 09:45:42 +0200 Subject: [PATCH 33/46] add unregister --- protocol/messenger.go | 2 +- protocol/push_notification_client/client.go | 72 +++++++++++++++++++ .../push_notification_server/server_test.go | 2 - 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 39f6b2a73..e075231b4 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3044,7 +3044,7 @@ func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ec } func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error { - return nil + return m.pushNotificationClient.Unregister() } func (m *Messenger) DisableSendingPushNotifications() error { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 5f13c30b6..3a706685e 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -456,6 +456,15 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu return options, nil } +func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotificationRegistration { + options := &protobuf.PushNotificationRegistration{ + Version: c.getVersion(), + InstallationId: c.config.InstallationID, + Unregister: true, + } + return options +} + // shouldRefreshToken tells us whether we should pull a new token, that's only necessary when a contact is removed func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.PublicKey) bool { newContactIDsMap := make(map[string]bool) @@ -481,6 +490,29 @@ func shouldRetryRegisteringWithServer(server *PushNotificationServer) bool { return time.Now().Unix() < nextServerRetry(server) } +func (c *Client) resetServers() error { + servers, err := c.persistence.GetServers() + if err != nil { + return err + } + for _, server := range servers { + + // Reset server registration data + server.Registered = false + server.RegisteredAt = 0 + server.RetryCount += 1 + server.LastRetriedAt = time.Now().Unix() + server.AccessToken = "" + + if err := c.persistence.UpsertServer(server); err != nil { + return err + } + + } + + return nil +} + func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegistration, server *PushNotificationServer) error { // Reset server registration data server.Registered = false @@ -576,6 +608,41 @@ func (c *Client) registrationLoop() error { } } +func (c *Client) Unregister() error { + // stop registration loop + c.stopRegistrationLoop() + + registration := c.buildPushNotificationUnregisterMessage() + err := c.SaveLastPushNotificationRegistration(registration, nil) + if err != nil { + return err + } + // Reset servers + err = c.resetServers() + if err != nil { + return err + } + + // and asynchronously register + c.startRegistrationLoop() + return nil +} + +func (c *Client) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error { + // stop registration loop + c.stopRegistrationLoop() + + err := c.persistence.SaveLastPushNotificationRegistration(registration, contactIDs) + if err != nil { + return err + } + c.lastPushNotificationRegistration = registration + c.lastContactIDs = contactIDs + + c.startRegistrationLoop() + return nil +} + func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]*PushNotificationServer, error) { // stop registration loop c.stopRegistrationLoop() @@ -595,6 +662,11 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut return nil, err } + err = c.SaveLastPushNotificationRegistration(registration, contactIDs) + if err != nil { + return nil, err + } + var serverPublicKeys []*ecdsa.PublicKey for _, server := range servers { err := c.registerWithServer(registration, server) diff --git a/protocol/push_notification_server/server_test.go b/protocol/push_notification_server/server_test.go index 4f31070e5..e895af70f 100644 --- a/protocol/push_notification_server/server_test.go +++ b/protocol/push_notification_server/server_test.go @@ -177,9 +177,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, - Grant: s.grant, Unregister: true, Version: 1, }) From 026e16533f03dab2778db3483a25a274b373d561 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 16 Jul 2020 10:36:17 +0200 Subject: [PATCH 34/46] Register asynchronously --- protocol/messenger.go | 18 ++- protocol/push_notification_client/client.go | 134 ++++++++------------ protocol/push_notification_test.go | 47 ++++--- services/ext/api.go | 12 +- 4 files changed, 110 insertions(+), 101 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index e075231b4..0414866c4 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3064,9 +3064,9 @@ func (m *Messenger) EnableSendingPushNotifications() error { } // RegisterForPushNotification register deviceToken with any push notification server enabled -func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { +func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { if m.pushNotificationClient == nil { - return nil, errors.New("push notification client not enabled") + return errors.New("push notification client not enabled") } var contactIDs []*ecdsa.PublicKey @@ -3095,6 +3095,20 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) } +func (m *Messenger) RegisteredForPushNotifications() (bool, error) { + if m.pushNotificationClient == nil { + return false, errors.New("no push notification client") + } + return m.pushNotificationClient.Registered() +} + +func (m *Messenger) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { + if m.pushNotificationClient == nil { + return nil, errors.New("no push notification client") + } + return m.pushNotificationClient.GetServers() +} + func (m *Messenger) StartPushNotificationServer() error { if m.pushNotificationServer == nil { pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(m.database) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 3a706685e..9839c557d 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -487,7 +487,7 @@ func nextServerRetry(server *PushNotificationServer) int64 { // We calculate if it's too early to retry, by exponentially backing off func shouldRetryRegisteringWithServer(server *PushNotificationServer) bool { - return time.Now().Unix() < nextServerRetry(server) + return time.Now().Unix() > nextServerRetry(server) } func (c *Client) resetServers() error { @@ -558,7 +558,7 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis func (c *Client) registrationLoop() error { for { - c.config.Logger.Info("runing registration loop") + c.config.Logger.Info("running registration loop") servers, err := c.persistence.GetServers() if err != nil { c.config.Logger.Error("failed retrieving servers, quitting registration loop", zap.Error(err)) @@ -571,39 +571,44 @@ func (c *Client) registrationLoop() error { var nonRegisteredServers []*PushNotificationServer for _, server := range servers { - if server.Registered { + if !server.Registered { nonRegisteredServers = append(nonRegisteredServers, server) } - if len(nonRegisteredServers) == 0 { - c.config.Logger.Debug("registered with all servers, quitting registration loop") - return nil - } + } + if len(nonRegisteredServers) == 0 { + c.config.Logger.Debug("registered with all servers, quitting registration loop") + return nil + } - var lowestNextRetry int64 + c.config.Logger.Info("Trying to register with", zap.Int("servers", len(nonRegisteredServers))) - for _, server := range nonRegisteredServers { - if shouldRetryRegisteringWithServer(server) { - err := c.registerWithServer(c.lastPushNotificationRegistration, server) - if err != nil { - return err - } + var lowestNextRetry int64 + + for _, server := range nonRegisteredServers { + nR := nextServerRetry(server) + c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nR)) + if shouldRetryRegisteringWithServer(server) { + c.config.Logger.Info("registering with server", zap.Any("server", server)) + err := c.registerWithServer(c.lastPushNotificationRegistration, server) + if err != nil { + return err } - nextRetry := nextServerRetry(server) - if lowestNextRetry == 0 || nextRetry < lowestNextRetry { - lowestNextRetry = nextRetry - } - + } + nextRetry := nextServerRetry(server) + if lowestNextRetry == 0 || nextRetry < lowestNextRetry { + lowestNextRetry = nextRetry } - nextRetry := lowestNextRetry - time.Now().Unix() - waitFor := time.Duration(nextRetry) - select { + } - case <-time.After(waitFor * time.Second): - case <-c.registrationLoopQuitChan: - return nil + nextRetry := lowestNextRetry - time.Now().Unix() + waitFor := time.Duration(nextRetry) + select { + + case <-time.After(waitFor * time.Second): + case <-c.registrationLoopQuitChan: + return nil - } } } } @@ -643,73 +648,44 @@ func (c *Client) SaveLastPushNotificationRegistration(registration *protobuf.Pus return nil } -func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) ([]*PushNotificationServer, error) { +func (c *Client) Registered() (bool, error) { + servers, err := c.persistence.GetServers() + if err != nil { + return false, err + } + + for _, s := range servers { + if !s.Registered { + return false, nil + } + } + + return true, nil +} + +func (c *Client) GetServers() ([]*PushNotificationServer, error) { + return c.persistence.GetServers() +} + +func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { // stop registration loop c.stopRegistrationLoop() c.DeviceToken = deviceToken - servers, err := c.persistence.GetServers() - if err != nil { - return nil, err - } - - if len(servers) == 0 { - return nil, errors.New("no servers to register with") - } registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) if err != nil { - return nil, err + return err } err = c.SaveLastPushNotificationRegistration(registration, contactIDs) if err != nil { - return nil, err + return err } - var serverPublicKeys []*ecdsa.PublicKey - for _, server := range servers { - err := c.registerWithServer(registration, server) - if err != nil { - return nil, err - } - serverPublicKeys = append(serverPublicKeys, server.PublicKey) - } + c.startRegistrationLoop() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - - // This code polls the database for server registrations, giving up - // after 5 seconds - for { - select { - case <-c.quit: - return servers, nil - case <-ctx.Done(): - c.config.Logger.Info("could not register all servers") - // start registration loop - c.startRegistrationLoop() - return servers, nil - case <-time.After(200 * time.Millisecond): - servers, err = c.persistence.GetServersByPublicKey(serverPublicKeys) - if err != nil { - return nil, err - } - - allRegistered := true - for _, server := range servers { - allRegistered = allRegistered && server.Registered - } - - // If any of the servers we haven't registered yet, continue - if !allRegistered { - continue - } - - // all have registered,cancel context and return - cancel() - return servers, nil - } - } + return nil } // HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 95961ff28..910794894 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -3,6 +3,7 @@ package protocol import ( "context" "crypto/ecdsa" + "errors" "io/ioutil" "os" "testing" @@ -114,11 +115,9 @@ func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Whi } func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { - errChan := make(chan error) bob1DeviceToken := "token-1" bob2DeviceToken := "token-2" - var bob1Servers, bob2Servers []*push_notification_client.PushNotificationServer bob1 := s.m bob2 := s.newMessengerWithKey(s.shh, s.m.identity) @@ -130,10 +129,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) - go func() { - bob1Servers, err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) - errChan <- err - }() + err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) // Receive message, reply // TODO: find a better way to handle this waiting @@ -163,21 +159,28 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { _, err = bob1.RetrieveAll() s.Require().NoError(err) + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + registered, err := bob1.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) // Make sure we receive it - err = <-errChan s.Require().NoError(err) - s.Require().NotNil(bob1Servers) - s.Require().Len(bob1Servers, 1) - s.Require().True(bob1Servers[0].Registered) + bob1Servers, err := bob1.GetPushNotificationServers() + s.Require().NoError(err) // Register bob2 err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) - go func() { - bob2Servers, err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) - errChan <- err - }() + err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) + s.Require().NoError(err) // Receive message, reply // TODO: find a better way to handle this waiting @@ -207,12 +210,20 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { _, err = bob2.RetrieveAll() s.Require().NoError(err) + err = tt.RetryWithBackOff(func() error { + registered, err := bob2.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) // Make sure we receive it - err = <-errChan s.Require().NoError(err) - s.Require().NotNil(bob2Servers) - s.Require().Len(bob2Servers, 1) - s.Require().True(bob2Servers[0].Registered) + bob2Servers, err := bob2.GetPushNotificationServers() + s.Require().NoError(err) err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) s.Require().NoError(err) diff --git a/services/ext/api.go b/services/ext/api.go index 468f01dab..62c9181e8 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -417,10 +417,10 @@ func (api *PublicAPI) StopPushNotificationServer() error { // PushNotification client -func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) ([]*push_notification_client.PushNotificationServer, error) { +func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled", true) if err != nil { - return nil, err + return err } return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken) } @@ -459,6 +459,14 @@ func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBy return api.service.messenger.AddPushNotificationServer(ctx, publicKey) } +func (api *PublicAPI) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { + return api.service.messenger.GetPushNotificationServers() +} + +func (api *PublicAPI) RegisteredForPushNotifications() (bool, error) { + return api.service.messenger.RegisteredForPushNotifications() +} + // Echo is a method for testing purposes. func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) { return message, nil From 12a3c5a31a06963cc0cb1f7ef4096836a9a53d13 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 17 Jul 2020 13:41:49 +0200 Subject: [PATCH 35/46] Add version to notification info --- PUSH-NOTIFICATIONS-TODO.txt | 1 + appdatabase/migrations/bindata.go | 8 +- .../sql/0008_add_push_notifications.up.sql | 3 +- multiaccounts/accounts/database.go | 96 +++++----- protocol/messenger.go | 24 +++ protocol/migrations/migrations.go | 4 +- protocol/protobuf/push_notifications.pb.go | 118 ++++++------ protocol/protobuf/push_notifications.proto | 1 + protocol/push_notification_client/client.go | 23 ++- .../migrations/migrations.go | 12 +- .../sql/1593601729_initial_schema.up.sql | 1 + .../push_notification_client/persistence.go | 23 ++- .../persistence_test.go | 69 +++++++ .../migrations/migrations.go | 6 +- protocol/push_notification_server/server.go | 1 + protocol/push_notification_test.go | 179 +++++++++++++++++- services/ext/api.go | 47 ++++- services/ext/service.go | 2 +- 18 files changed, 492 insertions(+), 126 deletions(-) diff --git a/PUSH-NOTIFICATIONS-TODO.txt b/PUSH-NOTIFICATIONS-TODO.txt index 2716e3e99..4e3af4fdf 100644 --- a/PUSH-NOTIFICATIONS-TODO.txt +++ b/PUSH-NOTIFICATIONS-TODO.txt @@ -3,3 +3,4 @@ Specs changes: - Encrypt of payload instead of signature + public key of server - Removed preferencs + each device registers individually - Add grant +- Add version in PushNotificationInfo diff --git a/appdatabase/migrations/bindata.go b/appdatabase/migrations/bindata.go index e2294942b..7af13be2b 100644 --- a/appdatabase/migrations/bindata.go +++ b/appdatabase/migrations/bindata.go @@ -12,7 +12,7 @@ // 0005_waku_mode.up.sql (146B) // 0006_appearance.up.sql (67B) // 0007_enable_waku_default.up.sql (38B) -// 0008_add_push_notifications.up.sql (214B) +// 0008_add_push_notifications.up.sql (294B) // doc.go (74B) package migrations @@ -322,7 +322,7 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) { return a, nil } -var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x4e\x2d\x29\xc9\xcc\x4b\x2f\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x4a\xcd\xcd\x2f\x49\x8d\x2f\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x2b\x8e\x4f\xcd\x4b\x4c\xca\x49\x4d\x51\x70\xf2\xf7\xf7\x71\x75\xf4\xb3\xe6\x22\x64\x54\x71\x6a\x5e\x0a\x16\x83\x88\x37\x00\x43\x6f\x7c\x71\x6a\x51\x59\x6a\x11\xa6\x53\x00\x01\x00\x00\xff\xff\xfc\xde\xba\xf0\xd6\x00\x00\x00") +var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xce\xc1\x0a\xc2\x30\x0c\x00\xd0\xbb\x5f\x91\xff\xf0\xd4\xb9\xde\xea\x0a\x32\xcf\xa1\x76\x99\x16\xd6\x44\x9a\x28\xf8\xf7\x1e\xbd\x14\xd4\x1f\x78\x3c\x17\x66\x7f\x82\xd9\x0d\xc1\x83\x92\x59\xe1\xab\x82\x1b\x47\x38\xc4\x70\x3e\x4e\xd0\xa8\x8a\x11\xde\x1f\x7a\x43\x16\x2b\x6b\xc9\xc9\x8a\xb0\x22\x71\xba\x6c\xb4\xc0\x10\x63\xf0\x6e\xda\xef\xbe\x51\x4a\xbc\x74\xa0\xdf\x81\x4e\x42\xa9\x3d\xa9\xfd\x7f\xe9\x50\x6b\x93\x8a\x59\xd8\x52\x36\x45\xe1\xed\xf5\xe1\xde\x01\x00\x00\xff\xff\x16\x96\xb2\xb5\x26\x01\x00\x00") func _0008_add_push_notificationsUpSqlBytes() ([]byte, error) { return bindataRead( @@ -337,8 +337,8 @@ func _0008_add_push_notificationsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 214, mode: os.FileMode(0644), modTime: time.Unix(1594815858, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0x77, 0x87, 0xd3, 0xd7, 0x50, 0x58, 0x80, 0x8f, 0x56, 0x4c, 0x2f, 0x1b, 0x58, 0x4d, 0x98, 0xdd, 0x3, 0xc5, 0x91, 0xad, 0x5e, 0xfe, 0xb, 0x54, 0xb3, 0x7e, 0xd8, 0xf7, 0x25, 0xa0, 0xda}} + info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 294, mode: os.FileMode(0644), modTime: time.Unix(1594974529, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x81, 0x4a, 0x9, 0x4d, 0xd9, 0xa1, 0x3d, 0xc5, 0xc3, 0xd7, 0x62, 0x8f, 0xc5, 0xfd, 0xb8, 0xa2, 0x71, 0x1d, 0xbc, 0xa6, 0x0, 0x3, 0x83, 0x7a, 0x12, 0x7b, 0x1f, 0xa9, 0x2, 0x29, 0x1a}} return a, nil } diff --git a/appdatabase/migrations/sql/0008_add_push_notifications.up.sql b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql index 2add23e03..c4bc5a993 100644 --- a/appdatabase/migrations/sql/0008_add_push_notifications.up.sql +++ b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql @@ -1,3 +1,4 @@ ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN; ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN; -ALTER TABLE settings ADD COLUMN push_notification_server_enabled BOOLEAN; +ALTER TABLE settings ADD COLUMN push_notifications_server_enabled BOOLEAN; +ALTER TABLE settings ADD COLUMN push_notifications_from_contacts_only BOOLEAN; diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index 09a79d1d1..69990e846 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -38,49 +38,50 @@ type Account struct { type Settings struct { // required - Address types.Address `json:"address"` - ChaosMode bool `json:"chaos-mode?,omitempty"` - Currency string `json:"currency,omitempty"` - CurrentNetwork string `json:"networks/current-network"` - CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` - CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` - DappsAddress types.Address `json:"dapps-address"` - EIP1581Address types.Address `json:"eip1581-address"` - Fleet *string `json:"fleet,omitempty"` - HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` - InstallationID string `json:"installation-id"` - KeyUID string `json:"key-uid"` - KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` - KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` - KeycardPairing string `json:"keycard-pairing,omitempty"` - LastUpdated *int64 `json:"last-updated,omitempty"` - LatestDerivedPath uint `json:"latest-derived-path"` - LogLevel *string `json:"log-level,omitempty"` - Mnemonic *string `json:"mnemonic,omitempty"` - Name string `json:"name,omitempty"` - Networks *json.RawMessage `json:"networks/networks"` - NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` - PhotoPath string `json:"photo-path"` - PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` - PreferredName *string `json:"preferred-name,omitempty"` - PreviewPrivacy bool `json:"preview-privacy?"` - PublicKey string `json:"public-key"` - PushNotificationServerEnabled bool `json:"push-notification-server-enabled,omitempty"` - RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` - RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled,omitempty"` - SigningPhrase string `json:"signing-phrase"` - StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` - StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` - StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` - SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` - SendPushNotifications bool `json:"send-push-notifications,omitempty"` - Appearance uint `json:"appearance"` - Usernames *json.RawMessage `json:"usernames,omitempty"` - WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` - WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` - WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` - WakuEnabled bool `json:"waku-enabled,omitempty"` - WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` + Address types.Address `json:"address"` + ChaosMode bool `json:"chaos-mode?,omitempty"` + Currency string `json:"currency,omitempty"` + CurrentNetwork string `json:"networks/current-network"` + CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` + CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` + DappsAddress types.Address `json:"dapps-address"` + EIP1581Address types.Address `json:"eip1581-address"` + Fleet *string `json:"fleet,omitempty"` + HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` + InstallationID string `json:"installation-id"` + KeyUID string `json:"key-uid"` + KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` + KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` + KeycardPairing string `json:"keycard-pairing,omitempty"` + LastUpdated *int64 `json:"last-updated,omitempty"` + LatestDerivedPath uint `json:"latest-derived-path"` + LogLevel *string `json:"log-level,omitempty"` + Mnemonic *string `json:"mnemonic,omitempty"` + Name string `json:"name,omitempty"` + Networks *json.RawMessage `json:"networks/networks"` + NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` + PhotoPath string `json:"photo-path"` + PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` + PreferredName *string `json:"preferred-name,omitempty"` + PreviewPrivacy bool `json:"preview-privacy?"` + PublicKey string `json:"public-key"` + PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled,omitempty"` + PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only,omitempty"` + RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` + RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled,omitempty"` + SigningPhrase string `json:"signing-phrase"` + StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` + StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` + StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` + SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` + SendPushNotifications bool `json:"send-push-notifications,omitempty"` + Appearance uint `json:"appearance"` + Usernames *json.RawMessage `json:"usernames,omitempty"` + WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` + WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` + WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` + WakuEnabled bool `json:"waku-enabled,omitempty"` + WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` } func NewDB(db *sql.DB) *Database { @@ -260,8 +261,13 @@ func (db *Database) SaveSetting(setting string, value interface{}) error { if !ok { return ErrInvalidConfig } - update, err = db.db.Prepare("UPDATE settings SET push_notification_server_enabled = ? WHERE synthetic_id = 'id'") - + update, err = db.db.Prepare("UPDATE settings SET push_notifications_server_enabled = ? WHERE synthetic_id = 'id'") + case "push-notifications-from-contacts-only": + _, ok := value.(bool) + if !ok { + return ErrInvalidConfig + } + update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'") case "send-push-notifications": _, ok := value.(bool) if !ok { diff --git a/protocol/messenger.go b/protocol/messenger.go index 0414866c4..0ac1dfae1 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3043,6 +3043,14 @@ func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ec return m.pushNotificationClient.AddPushNotificationServer(publicKey) } +// RemovePushNotificationServer removes a push notification server +func (m *Messenger) RemovePushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + return m.pushNotificationClient.RemovePushNotificationServer(publicKey) +} + func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error { return m.pushNotificationClient.Unregister() } @@ -3102,6 +3110,22 @@ func (m *Messenger) RegisteredForPushNotifications() (bool, error) { return m.pushNotificationClient.Registered() } +func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { + if m.pushNotificationClient == nil { + return errors.New("no push notification client") + } + + return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly() +} + +func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { + if m.pushNotificationClient == nil { + return errors.New("no push notification client") + } + + return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly() +} + func (m *Messenger) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { if m.pushNotificationClient == nil { return nil, errors.New("no push notification client") diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index a668f1cf7..eeedb604e 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -338,7 +338,7 @@ func _1593087212_add_mute_chatDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1594393604, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1594896559, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -358,7 +358,7 @@ func _1593087212_add_mute_chatUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1594393604, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1594896559, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe, 0x9, 0xa0, 0x4a, 0x8e, 0x23, 0xe4, 0xce, 0xbc, 0xd4, 0x9, 0xeb, 0xf9, 0x67, 0x90, 0xc0, 0x4b, 0x67, 0x84, 0xe4, 0x42, 0x8d, 0x0, 0x17, 0x29, 0x7f, 0x12, 0xbf, 0x7d, 0x4e, 0x78, 0xec}} return a, nil } diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index d3ca7b1d7..3b2b03a57 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -418,6 +418,7 @@ type PushNotificationQueryInfo struct { PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` AllowedUserList [][]byte `protobuf:"bytes,4,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` Grant []byte `protobuf:"bytes,5,opt,name=grant,proto3" json:"grant,omitempty"` + Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -483,6 +484,13 @@ func (m *PushNotificationQueryInfo) GetGrant() []byte { return nil } +func (m *PushNotificationQueryInfo) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + type PushNotificationQueryResponse struct { Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` @@ -786,59 +794,59 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 856 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xe1, 0x6e, 0xe3, 0x44, - 0x10, 0xc7, 0x59, 0x27, 0x6d, 0xe2, 0x69, 0x68, 0x73, 0xab, 0x5e, 0xcf, 0x9c, 0x38, 0xc8, 0x19, - 0x24, 0xa2, 0x43, 0x8a, 0x50, 0x91, 0xe0, 0xc4, 0x27, 0x42, 0xeb, 0x16, 0xab, 0x8d, 0x1d, 0x36, - 0x2e, 0x27, 0x24, 0x24, 0xcb, 0x89, 0xb7, 0x8d, 0xd5, 0x9c, 0x6d, 0x76, 0xd7, 0x87, 0xf2, 0x01, - 0x89, 0x27, 0x40, 0xe2, 0x2b, 0x4f, 0x81, 0x78, 0x0a, 0xde, 0x81, 0x97, 0x41, 0x5e, 0xdb, 0xc1, - 0x71, 0x7c, 0x69, 0x3e, 0xf0, 0x29, 0x9e, 0xd9, 0x99, 0xdd, 0x9d, 0xdf, 0xec, 0xfc, 0x03, 0x5a, - 0x9c, 0xf0, 0xb9, 0x1b, 0x46, 0x22, 0xb8, 0x0d, 0x66, 0x9e, 0x08, 0xa2, 0x90, 0x0f, 0x62, 0x16, - 0x89, 0x08, 0xb7, 0xe5, 0xcf, 0x34, 0xb9, 0xd5, 0xff, 0x69, 0xc0, 0xfb, 0xe3, 0x84, 0xcf, 0xad, - 0x52, 0x14, 0xa1, 0x77, 0x01, 0x17, 0x4c, 0x7e, 0x63, 0x1b, 0x40, 0x44, 0xf7, 0x34, 0x74, 0xc5, - 0x32, 0xa6, 0x1a, 0xea, 0xa1, 0xfe, 0xe1, 0xe9, 0x67, 0x83, 0x22, 0x7f, 0xb0, 0x2d, 0x77, 0xe0, - 0xa4, 0x89, 0xce, 0x32, 0xa6, 0x44, 0x15, 0xc5, 0x27, 0x3e, 0x86, 0x3d, 0x69, 0x68, 0x4a, 0x0f, - 0xf5, 0x55, 0x92, 0x19, 0xf8, 0x13, 0x38, 0x0a, 0x42, 0x2e, 0xbc, 0xc5, 0x42, 0xa6, 0xba, 0x81, - 0xaf, 0x35, 0xe4, 0xfa, 0x61, 0xd9, 0x6d, 0xfa, 0xf8, 0x39, 0x74, 0xbc, 0xd9, 0x8c, 0x72, 0xee, - 0x66, 0xbb, 0x34, 0x65, 0xd4, 0x41, 0xe6, 0x93, 0x07, 0x62, 0x0d, 0x5a, 0x34, 0xf4, 0xa6, 0x0b, - 0xea, 0x6b, 0x7b, 0x3d, 0xd4, 0x6f, 0x93, 0xc2, 0x4c, 0x57, 0xde, 0x50, 0xc6, 0x83, 0x28, 0xd4, - 0xf6, 0x7b, 0xa8, 0xdf, 0x24, 0x85, 0x89, 0x5f, 0xc0, 0x23, 0x6f, 0xb1, 0x88, 0x7e, 0xa6, 0xbe, - 0x9b, 0x70, 0xca, 0xdc, 0x45, 0xc0, 0x85, 0xd6, 0xea, 0x35, 0xfa, 0x1d, 0x72, 0x94, 0x2f, 0xdc, - 0x70, 0xca, 0xae, 0x03, 0x2e, 0xd2, 0xd8, 0xe9, 0x22, 0x9a, 0xdd, 0x53, 0xdf, 0x9d, 0xcd, 0x3d, - 0x91, 0xc5, 0xb6, 0xb3, 0xd8, 0x7c, 0xe1, 0x6c, 0xee, 0x09, 0x19, 0xfb, 0x01, 0x40, 0x12, 0x32, - 0x09, 0x85, 0x32, 0x4d, 0x95, 0xd7, 0x29, 0x79, 0x52, 0x1a, 0x77, 0xcc, 0x0b, 0x85, 0x06, 0x3d, - 0xd4, 0xef, 0x90, 0xcc, 0xd0, 0x2f, 0x40, 0x5d, 0xb1, 0xc3, 0x27, 0x80, 0x6f, 0xac, 0x2b, 0xcb, - 0x7e, 0x65, 0xb9, 0x8e, 0x7d, 0x65, 0x58, 0xae, 0xf3, 0xc3, 0xd8, 0xe8, 0xbe, 0x83, 0xdf, 0x05, - 0x75, 0x38, 0xce, 0x7d, 0x5d, 0x84, 0x31, 0x1c, 0x5e, 0x98, 0xc4, 0xf8, 0x66, 0x38, 0x31, 0x72, - 0x9f, 0xa2, 0xff, 0xa5, 0xc0, 0xc7, 0xdb, 0x3a, 0x44, 0x28, 0x8f, 0xa3, 0x90, 0xd3, 0x14, 0x0c, - 0x4f, 0x24, 0x42, 0xd9, 0xe2, 0x36, 0x29, 0x4c, 0x6c, 0xc1, 0x1e, 0x65, 0x2c, 0x62, 0xb2, 0x5d, - 0x87, 0xa7, 0x2f, 0x77, 0x6b, 0x7d, 0xb1, 0xf1, 0xc0, 0x48, 0x73, 0xe5, 0x13, 0xc8, 0xb6, 0xc1, - 0xcf, 0x00, 0x18, 0xfd, 0x29, 0xa1, 0x5c, 0x14, 0x3d, 0xee, 0x10, 0x35, 0xf7, 0x98, 0xbe, 0xfe, - 0x2b, 0x02, 0x75, 0x95, 0x53, 0x2e, 0xdd, 0x20, 0xc4, 0x26, 0x45, 0xe9, 0x8f, 0xe1, 0xd1, 0x68, - 0x78, 0x7d, 0x61, 0x93, 0x91, 0x71, 0xee, 0x8e, 0x8c, 0xc9, 0x64, 0x78, 0x69, 0x74, 0x11, 0x3e, - 0x86, 0xee, 0xf7, 0x06, 0x99, 0x98, 0xb6, 0xe5, 0x8e, 0xcc, 0xc9, 0x68, 0xe8, 0x9c, 0x7d, 0xdb, - 0x55, 0xf0, 0x53, 0x38, 0xb9, 0xb1, 0x26, 0x37, 0xe3, 0xb1, 0x4d, 0x1c, 0xe3, 0xbc, 0xcc, 0xb0, - 0x91, 0x42, 0x33, 0x2d, 0xc7, 0x20, 0xd6, 0xf0, 0x3a, 0x3b, 0xa1, 0xdb, 0xd4, 0x7f, 0x43, 0xf0, - 0xbc, 0x5a, 0xdb, 0xd0, 0x7f, 0x43, 0x99, 0x08, 0x38, 0x7d, 0x4d, 0x43, 0x61, 0x86, 0xb7, 0x51, - 0x5a, 0x47, 0x9c, 0x4c, 0x17, 0xc1, 0xcc, 0xbd, 0xa7, 0x4b, 0x09, 0xad, 0x43, 0xd4, 0xcc, 0x73, - 0x45, 0x97, 0x1b, 0xcf, 0x54, 0xd9, 0x7c, 0xa6, 0xbb, 0x3e, 0x79, 0xfd, 0x17, 0xd0, 0xce, 0xa2, - 0x50, 0x78, 0x33, 0x71, 0x16, 0xf9, 0x74, 0xed, 0x2a, 0xd8, 0x83, 0x93, 0x8d, 0x29, 0x77, 0x83, - 0xf0, 0x36, 0xd2, 0x50, 0xaf, 0xd1, 0x3f, 0x38, 0xfd, 0xf4, 0xed, 0xfd, 0xda, 0xa8, 0x89, 0x1c, - 0xc7, 0x95, 0x90, 0xd4, 0xab, 0xbf, 0x84, 0xc7, 0xd5, 0xd4, 0xef, 0x12, 0xca, 0x96, 0xf8, 0x43, - 0x38, 0xf8, 0x0f, 0x01, 0x97, 0x07, 0x76, 0x08, 0xac, 0x18, 0x70, 0xfd, 0x6f, 0x04, 0xef, 0xd5, - 0xa6, 0x4a, 0x82, 0x55, 0x44, 0x68, 0x27, 0x44, 0x4a, 0xad, 0x2a, 0xac, 0x77, 0xa3, 0x51, 0xed, - 0x46, 0xed, 0x74, 0x37, 0xeb, 0xa7, 0x7b, 0x35, 0x91, 0x7b, 0xe5, 0x89, 0xfc, 0x1d, 0xc1, 0xb3, - 0xda, 0x52, 0x56, 0x23, 0xf4, 0x25, 0x34, 0x4b, 0xdc, 0x3f, 0x7a, 0x3b, 0xf7, 0x15, 0x01, 0x22, - 0x13, 0xd2, 0xbb, 0xbf, 0xa6, 0x9c, 0x7b, 0x77, 0xb4, 0xa8, 0xaf, 0x43, 0xd4, 0xdc, 0x63, 0xfa, - 0xe5, 0xd1, 0x6c, 0xac, 0x8d, 0xa6, 0xfe, 0x27, 0x82, 0x6e, 0x75, 0xf3, 0x5d, 0xa8, 0x3e, 0x81, - 0x96, 0xd4, 0xad, 0x15, 0xcd, 0xfd, 0xd4, 0x7c, 0x98, 0x62, 0x4d, 0x37, 0x9a, 0xb5, 0xdd, 0xd0, - 0xa0, 0x95, 0xdf, 0x3f, 0x87, 0x58, 0x98, 0x7a, 0x0c, 0x4f, 0x36, 0x65, 0x43, 0xce, 0x3e, 0xfe, - 0x02, 0xda, 0xb9, 0x0c, 0xf0, 0x9c, 0xe1, 0xd3, 0x2d, 0x5a, 0xb3, 0x8a, 0x7d, 0x00, 0x9f, 0xfe, - 0x87, 0x02, 0x27, 0x9b, 0x47, 0xc6, 0x11, 0x13, 0x5b, 0x44, 0xef, 0xeb, 0x75, 0xd1, 0x7b, 0xb1, - 0x4d, 0xf4, 0xd2, 0xad, 0x6a, 0x65, 0xee, 0xff, 0x40, 0xa9, 0xff, 0xb8, 0x8b, 0x1c, 0x1e, 0xc1, - 0xc1, 0x2b, 0x62, 0x5b, 0x97, 0xe5, 0xff, 0x82, 0x8a, 0xac, 0x29, 0xa9, 0xcf, 0xb2, 0x1d, 0x97, - 0x18, 0x97, 0xe6, 0xc4, 0x31, 0x88, 0x71, 0xde, 0x6d, 0xe8, 0x09, 0x68, 0x9b, 0x05, 0xe5, 0xef, - 0x79, 0x9d, 0x2b, 0xaa, 0x3e, 0xcb, 0xaf, 0xa0, 0xc5, 0x64, 0xed, 0x5c, 0x53, 0x64, 0xb7, 0x7a, - 0x0f, 0x41, 0x22, 0x45, 0xc2, 0x74, 0x5f, 0x46, 0x7e, 0xfe, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, - 0xcc, 0x3e, 0xe7, 0x96, 0xa1, 0x08, 0x00, 0x00, + // 858 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x51, 0x6f, 0xe3, 0x44, + 0x10, 0x66, 0x9d, 0xb4, 0x89, 0xa7, 0xa1, 0xcd, 0xad, 0x7a, 0x3d, 0x73, 0xe2, 0x20, 0x67, 0x90, + 0x88, 0x0e, 0x29, 0x42, 0x45, 0x82, 0x13, 0x4f, 0x84, 0xd6, 0x2d, 0x56, 0x1b, 0x3b, 0x6c, 0x5c, + 0x4e, 0x48, 0x48, 0x96, 0x13, 0x6f, 0x5b, 0xab, 0x39, 0xdb, 0xec, 0xae, 0x8b, 0xf2, 0x80, 0xc4, + 0x2f, 0x40, 0xe2, 0x95, 0x5f, 0x81, 0xf8, 0x3b, 0xbc, 0xf1, 0x4b, 0x90, 0xd7, 0x76, 0x70, 0x1c, + 0x37, 0xcd, 0x03, 0x4f, 0xf1, 0xcc, 0xce, 0xcc, 0xee, 0x7c, 0xdf, 0xcc, 0x17, 0xd0, 0xe2, 0x84, + 0xdf, 0xba, 0x61, 0x24, 0x82, 0xeb, 0x60, 0xe6, 0x89, 0x20, 0x0a, 0xf9, 0x20, 0x66, 0x91, 0x88, + 0x70, 0x5b, 0xfe, 0x4c, 0x93, 0x6b, 0xfd, 0xef, 0x06, 0xbc, 0x3f, 0x4e, 0xf8, 0xad, 0x55, 0x8a, + 0x22, 0xf4, 0x26, 0xe0, 0x82, 0xc9, 0x6f, 0x6c, 0x03, 0x88, 0xe8, 0x8e, 0x86, 0xae, 0x58, 0xc4, + 0x54, 0x43, 0x3d, 0xd4, 0xdf, 0x3f, 0xfe, 0x6c, 0x50, 0xe4, 0x0f, 0x36, 0xe5, 0x0e, 0x9c, 0x34, + 0xd1, 0x59, 0xc4, 0x94, 0xa8, 0xa2, 0xf8, 0xc4, 0x87, 0xb0, 0x23, 0x0d, 0x4d, 0xe9, 0xa1, 0xbe, + 0x4a, 0x32, 0x03, 0x7f, 0x02, 0x07, 0x41, 0xc8, 0x85, 0x37, 0x9f, 0xcb, 0x54, 0x37, 0xf0, 0xb5, + 0x86, 0x3c, 0xdf, 0x2f, 0xbb, 0x4d, 0x1f, 0xbf, 0x84, 0x8e, 0x37, 0x9b, 0x51, 0xce, 0xdd, 0xac, + 0x4a, 0x53, 0x46, 0xed, 0x65, 0x3e, 0x79, 0x21, 0xd6, 0xa0, 0x45, 0x43, 0x6f, 0x3a, 0xa7, 0xbe, + 0xb6, 0xd3, 0x43, 0xfd, 0x36, 0x29, 0xcc, 0xf4, 0xe4, 0x9e, 0x32, 0x1e, 0x44, 0xa1, 0xb6, 0xdb, + 0x43, 0xfd, 0x26, 0x29, 0x4c, 0xfc, 0x0a, 0x9e, 0x78, 0xf3, 0x79, 0xf4, 0x33, 0xf5, 0xdd, 0x84, + 0x53, 0xe6, 0xce, 0x03, 0x2e, 0xb4, 0x56, 0xaf, 0xd1, 0xef, 0x90, 0x83, 0xfc, 0xe0, 0x8a, 0x53, + 0x76, 0x19, 0x70, 0x91, 0xc6, 0x4e, 0xe7, 0xd1, 0xec, 0x8e, 0xfa, 0xee, 0xec, 0xd6, 0x13, 0x59, + 0x6c, 0x3b, 0x8b, 0xcd, 0x0f, 0x4e, 0x6e, 0x3d, 0x21, 0x63, 0x3f, 0x00, 0x48, 0x42, 0x26, 0x41, + 0xa1, 0x4c, 0x53, 0xe5, 0x73, 0x4a, 0x9e, 0x14, 0x8d, 0x1b, 0xe6, 0x85, 0x42, 0x83, 0x1e, 0xea, + 0x77, 0x48, 0x66, 0xe8, 0x67, 0xa0, 0x2e, 0xb1, 0xc3, 0x47, 0x80, 0xaf, 0xac, 0x0b, 0xcb, 0x7e, + 0x63, 0xb9, 0x8e, 0x7d, 0x61, 0x58, 0xae, 0xf3, 0xc3, 0xd8, 0xe8, 0xbe, 0x83, 0xdf, 0x05, 0x75, + 0x38, 0xce, 0x7d, 0x5d, 0x84, 0x31, 0xec, 0x9f, 0x99, 0xc4, 0xf8, 0x66, 0x38, 0x31, 0x72, 0x9f, + 0xa2, 0xff, 0xa5, 0xc0, 0xc7, 0x9b, 0x18, 0x22, 0x94, 0xc7, 0x51, 0xc8, 0x69, 0x0a, 0x0c, 0x4f, + 0x24, 0x84, 0x92, 0xe2, 0x36, 0x29, 0x4c, 0x6c, 0xc1, 0x0e, 0x65, 0x2c, 0x62, 0x92, 0xae, 0xfd, + 0xe3, 0xd7, 0xdb, 0x51, 0x5f, 0x14, 0x1e, 0x18, 0x69, 0xae, 0x1c, 0x81, 0xac, 0x0c, 0x7e, 0x01, + 0xc0, 0xe8, 0x4f, 0x09, 0xe5, 0xa2, 0xe0, 0xb8, 0x43, 0xd4, 0xdc, 0x63, 0xfa, 0xfa, 0xaf, 0x08, + 0xd4, 0x65, 0x4e, 0xb9, 0x75, 0x83, 0x10, 0x9b, 0x14, 0xad, 0x3f, 0x85, 0x27, 0xa3, 0xe1, 0xe5, + 0x99, 0x4d, 0x46, 0xc6, 0xa9, 0x3b, 0x32, 0x26, 0x93, 0xe1, 0xb9, 0xd1, 0x45, 0xf8, 0x10, 0xba, + 0xdf, 0x1b, 0x64, 0x62, 0xda, 0x96, 0x3b, 0x32, 0x27, 0xa3, 0xa1, 0x73, 0xf2, 0x6d, 0x57, 0xc1, + 0xcf, 0xe1, 0xe8, 0xca, 0x9a, 0x5c, 0x8d, 0xc7, 0x36, 0x71, 0x8c, 0xd3, 0x32, 0x86, 0x8d, 0x14, + 0x34, 0xd3, 0x72, 0x0c, 0x62, 0x0d, 0x2f, 0xb3, 0x1b, 0xba, 0x4d, 0xfd, 0x37, 0x04, 0x2f, 0xab, + 0xbd, 0x0d, 0xfd, 0x7b, 0xca, 0x44, 0xc0, 0xe9, 0x5b, 0x1a, 0x0a, 0x33, 0xbc, 0x8e, 0xd2, 0x3e, + 0xe2, 0x64, 0x3a, 0x0f, 0x66, 0xee, 0x1d, 0x5d, 0x48, 0xd0, 0x3a, 0x44, 0xcd, 0x3c, 0x17, 0x74, + 0xb1, 0x36, 0xa6, 0xca, 0xfa, 0x98, 0x6e, 0x3b, 0xf2, 0xfa, 0x2f, 0xa0, 0x9d, 0x44, 0xa1, 0xf0, + 0x66, 0xe2, 0x24, 0xf2, 0xe9, 0xca, 0x53, 0xb0, 0x07, 0x47, 0x6b, 0x5b, 0xee, 0x06, 0xe1, 0x75, + 0xa4, 0xa1, 0x5e, 0xa3, 0xbf, 0x77, 0xfc, 0xe9, 0xc3, 0x7c, 0xad, 0xf5, 0x44, 0x0e, 0xe3, 0x4a, + 0x48, 0xea, 0xd5, 0x5f, 0xc3, 0xd3, 0x6a, 0xea, 0x77, 0x09, 0x65, 0x0b, 0xfc, 0x21, 0xec, 0xfd, + 0x07, 0x01, 0x97, 0x17, 0x76, 0x08, 0x2c, 0x31, 0xe0, 0xfa, 0x3f, 0x08, 0xde, 0xab, 0x4d, 0x95, + 0x08, 0x56, 0x21, 0x42, 0x5b, 0x41, 0xa4, 0xd4, 0xaa, 0xc2, 0x2a, 0x1b, 0x8d, 0x2a, 0x1b, 0xb5, + 0xdb, 0xdd, 0xac, 0xdf, 0xee, 0xe5, 0x46, 0xee, 0x94, 0x36, 0xf2, 0x61, 0xe5, 0xd0, 0x7f, 0x47, + 0xf0, 0xa2, 0xb6, 0xc9, 0xe5, 0x72, 0x7d, 0x09, 0xcd, 0x12, 0x23, 0x1f, 0x3d, 0xcc, 0xc8, 0x12, + 0x1b, 0x22, 0x13, 0xd2, 0xae, 0xde, 0x52, 0xce, 0xbd, 0x1b, 0x5a, 0x74, 0xde, 0x21, 0x6a, 0xee, + 0x31, 0xfd, 0xf2, 0xd2, 0x36, 0x56, 0x96, 0x56, 0xff, 0x13, 0x41, 0xb7, 0x5a, 0x7c, 0x1b, 0xbc, + 0x9f, 0x41, 0x4b, 0x2a, 0xda, 0x12, 0xe7, 0xdd, 0xd4, 0x7c, 0x1c, 0xdf, 0x1a, 0x9e, 0x9a, 0xb5, + 0x3c, 0x69, 0xd0, 0xca, 0xdf, 0x9f, 0xc3, 0x5b, 0x98, 0x7a, 0x0c, 0xcf, 0xd6, 0x05, 0x45, 0xaa, + 0x02, 0xfe, 0x02, 0xda, 0xb9, 0x40, 0xf0, 0x1c, 0xc3, 0xe7, 0x1b, 0x54, 0x68, 0x19, 0xfb, 0x08, + 0x7c, 0xfa, 0x1f, 0x0a, 0x1c, 0xad, 0x5f, 0x19, 0x47, 0x4c, 0x6c, 0x90, 0xc3, 0xaf, 0x57, 0xe5, + 0xf0, 0xd5, 0x26, 0x39, 0x4c, 0x4b, 0xd5, 0x0a, 0xe0, 0xff, 0x01, 0xa5, 0xfe, 0xe3, 0x36, 0x42, + 0x79, 0x00, 0x7b, 0x6f, 0x88, 0x6d, 0x9d, 0x97, 0xff, 0x25, 0x2a, 0x82, 0xa7, 0xa4, 0x3e, 0xcb, + 0x76, 0x5c, 0x62, 0x9c, 0x9b, 0x13, 0xc7, 0x20, 0xc6, 0x69, 0xb7, 0xa1, 0x27, 0xa0, 0xad, 0x37, + 0x94, 0xcf, 0xf3, 0x2a, 0xae, 0xa8, 0x3a, 0x96, 0x5f, 0x41, 0x8b, 0xc9, 0xde, 0xb9, 0xa6, 0x48, + 0xb6, 0x7a, 0x8f, 0x81, 0x44, 0x8a, 0x84, 0xe9, 0xae, 0x8c, 0xfc, 0xfc, 0xdf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x04, 0xbd, 0xc3, 0x50, 0xbb, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index 577f06879..612dfc678 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -54,6 +54,7 @@ message PushNotificationQueryInfo { bytes public_key = 3; repeated bytes allowed_user_list = 4; bytes grant = 5; + uint64 version = 6; } message PushNotificationQueryResponse { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 9839c557d..cda929a05 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -64,6 +64,7 @@ type PushNotificationInfo struct { PublicKey *ecdsa.PublicKey ServerPublicKey *ecdsa.PublicKey RetrievedAt int64 + Version uint64 } type Config struct { @@ -74,9 +75,9 @@ type Config struct { // RemoteNotificationsEnabled is whether we should register with a remote server for push notifications RemoteNotificationsEnabled bool - // AllowOnlyFromContacts indicates whether we should be receiving push notifications + // AllowyFromContactsOnly indicates whether we should be receiving push notifications // only from contacts - AllowOnlyFromContacts bool + AllowFromContactsOnly bool // InstallationID is the installation-id for this device InstallationID string @@ -786,6 +787,7 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ ServerPublicKey: serverPublicKey, AccessToken: info.AccessToken, InstallationID: info.InstallationId, + Version: info.Version, RetrievedAt: time.Now().Unix(), }) @@ -804,6 +806,13 @@ func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationRe return nil } +func (c *Client) RemovePushNotificationServer(publicKey *ecdsa.PublicKey) error { + c.config.Logger.Info("removing push notification server", zap.Any("public-key", publicKey)) + //TODO: this needs implementing. It requires unregistering from the server and + // likely invalidate the device token of the user + return errors.New("not implemented") +} + func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { c.config.Logger.Info("adding push notification server", zap.Any("public-key", publicKey)) currentServers, err := c.persistence.GetServers() @@ -872,6 +881,16 @@ func (c *Client) DisableSending() { c.config.SendEnabled = false } +func (c *Client) EnablePushNotificationsFromContactsOnly() error { + c.config.AllowFromContactsOnly = true + return nil +} + +func (c *Client) DisablePushNotificationsFromContactsOnly() error { + c.config.AllowFromContactsOnly = false + return nil +} + func (c *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { encodedPublicKey := hex.EncodeToString(hashedPublicKey) return c.messageProcessor.JoinPublic(encodedPublicKey) diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index d6c7865d8..35f6e5633 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.576kB) +// 1593601729_initial_schema.up.sql (1.6kB) // doc.go (382B) package migrations @@ -86,12 +86,12 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa, 0x95, 0x55, 0x64, 0x38, 0x40, 0x16, 0xbf, 0x8b, 0x1c, 0x18, 0xb4, 0xc5, 0x7f, 0xd0, 0xb8, 0xf0, 0x3c, 0xa2, 0x82, 0xf8, 0x8d, 0x5a, 0xd3, 0xb6, 0x6e, 0xa3, 0xb4, 0xc, 0x9, 0x33, 0x0}} return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x8e\x9b\x30\x10\x86\xef\x3c\xc5\x1c\x17\x89\x43\xef\x7b\x02\xd6\x54\x48\x96\xdd\x26\x46\xca\xcd\x72\x8d\x77\x63\x85\x9a\xad\x6d\x56\xe5\xed\x2b\x60\xc9\x92\x90\x40\x95\xe4\x82\x84\xfd\x7b\xec\x6f\xfe\x99\x49\x37\x28\x66\x08\x58\x9c\x60\x04\x79\x06\x84\x32\x40\xbb\x7c\xcb\xb6\xf0\xde\xb8\x3d\x37\xb5\xd7\xaf\x5a\x0a\xaf\x6b\xc3\x65\xa5\x95\xf1\xdc\x29\xfb\xa1\xac\x83\xa7\x00\xe0\xbd\xf9\x55\x69\xc9\x0f\xaa\x85\x04\xd3\xa4\x3f\x4f\x0a\x8c\xa3\x00\xc0\xaa\x37\xed\xbc\xb2\xaa\x84\x84\x52\x8c\x62\x02\x2f\x28\x8b\x0b\xcc\x20\x8b\xf1\x16\x9d\x6a\xb8\xf0\x90\x13\x76\x8c\x70\xd4\x7e\xeb\x74\x95\x70\x9e\x5b\xe5\xad\x5e\x53\x76\xa2\x96\xcb\xba\x31\x4b\x2a\x21\xa5\x72\x8e\xfb\xfa\xa0\x0c\x30\xb4\x63\xdd\x62\x41\xf2\x9f\x05\x7a\xfa\x62\x0a\x81\x12\x48\x29\xc9\x70\x9e\x32\xd8\xa0\x1f\x38\x4e\x51\x10\x3e\x07\xc1\x2d\x79\xfb\xd3\x28\xab\xd5\x7a\xde\x06\xdd\x0c\x73\xdc\x6a\xb9\x2e\xe7\x87\x66\x6f\x8f\x46\xed\x63\x21\xb4\x79\xad\x57\x09\x86\x0a\xe1\x4b\x12\x6d\x9c\x17\x55\x35\xc4\xd6\x65\xef\xc1\x89\x60\xe6\xd0\x59\x6d\x75\xa5\xf0\x71\x39\x4b\xf3\x5c\x9c\x5f\x17\xcd\x9f\xf8\xd8\x34\x79\x2b\xe4\x41\x95\xfc\xb7\x72\x4e\xbc\x7d\x9a\xfe\xf9\x73\xd1\x3f\xb9\x17\xfe\x62\x1e\xc6\x48\xd7\x39\xbf\xc2\x9e\x32\xe4\xdf\x09\xdd\xa0\x00\xe0\x56\x08\xd7\x7d\xa6\x1b\xeb\x18\x77\x59\xde\xdf\xf7\x3f\x9c\x11\x2c\x78\x1b\xde\x41\x3c\x8c\x23\x3b\x81\x1d\x47\xd4\xb0\x36\x87\x02\x90\xb5\xf1\x42\x76\xe6\xb9\x7e\x7b\x58\x75\xad\xf1\x7b\xe5\xb5\xec\x48\xaf\xcf\xa1\x23\xdc\x54\xbf\x5a\x8a\x39\x79\x41\x3b\xd0\xe5\x5f\xbe\xd8\xa7\xd3\x06\xa4\x64\xb9\xa7\x97\xba\x25\x7c\x0e\xfe\x05\x00\x00\xff\xff\x58\xe5\xb5\x4b\x28\x06\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x6e\xa3\x30\x10\x86\xef\x3c\xc5\x1c\x1b\x29\x87\xbd\xf7\x44\xa8\x59\x21\x59\xf6\x6e\x62\xa4\xdc\x2c\xaf\x71\x1b\x2b\xac\xe9\xda\xa6\x5a\xde\x7e\x65\x08\x29\xa9\x59\xa8\xda\x5e\x22\x65\xfc\x7b\xf0\x37\xf3\xcf\x64\x7b\x94\x32\x04\x2c\xdd\x61\x04\x45\x0e\x84\x32\x40\xc7\xe2\xc0\x0e\xf0\xdc\xba\x13\x37\x8d\xd7\x8f\x5a\x0a\xaf\x1b\xc3\x65\xad\x95\xf1\xdc\x29\xfb\xa2\xac\x83\xbb\x04\xe0\xb9\xfd\x55\x6b\xc9\xcf\xaa\x83\x1d\xa6\xbb\xfe\x3e\x29\x31\xde\x26\x00\x56\x3d\x69\xe7\x95\x55\x15\xec\x28\xc5\x28\x25\xf0\x80\xf2\xb4\xc4\x0c\xf2\x14\x1f\xd0\xad\x86\x0b\x0f\x05\x61\xd7\x0c\x57\xed\xb7\xa0\xab\x85\xf3\xdc\x2a\x6f\xf5\x9a\x32\x88\x3a\x2e\x9b\xd6\x2c\xa9\x84\x94\xca\x39\xee\x9b\xb3\x32\xc0\xd0\x91\x85\x60\x49\x8a\x9f\x25\xba\x7b\x65\xda\x00\x25\x90\x51\x92\xe3\x22\x63\xb0\x47\x3f\x70\x9a\xa1\x64\x73\x9f\x24\x1f\xa9\xdb\x9f\x56\x59\xad\xd6\xeb\x36\xe8\x22\xcc\xf1\xa8\xe3\xba\x8a\x2f\x45\x6f\xdf\x8e\xda\xaf\x85\xd0\xe6\xb1\x59\x25\x18\x1c\xc2\x97\x24\xda\x38\x2f\xea\x7a\xc8\xad\xab\xbe\x07\x37\x82\xa8\x43\x6f\xbc\x15\xac\xf0\x32\x5f\xa5\xe0\x4e\xdd\x98\x28\x1e\xd7\xe8\xed\x33\xb6\xf1\xd3\xbf\xb6\x7c\xde\x0a\x79\x56\x15\xff\xad\x9c\x13\x4f\x17\x33\x5c\xfe\xcc\xf6\x55\x9e\x84\x9f\xad\xcf\x98\x69\x86\xff\xc2\xf9\x9a\xf6\x96\xa1\xf8\x4e\xe8\x1e\x25\x00\x1f\x85\x70\xe1\x67\x7a\xb0\x8e\xf1\x29\x2b\xf4\xdf\x7b\x0f\xe7\x16\x16\x7a\xbb\xf9\x04\xf1\xb0\xa6\xec\x04\x76\x5c\x5d\x43\x2c\x86\x02\x90\x8d\xf1\x42\x86\xe6\xb9\xfe\x78\x88\xba\xce\xf8\x93\xf2\x5a\x06\xd2\xff\xef\xa7\x2b\xdc\x54\xbf\x6a\xc5\x82\x3c\xa0\x23\xe8\xea\x2f\x5f\x9c\xdf\xe9\x60\x52\xb2\x3c\xeb\x4b\xd3\xb2\xb9\x4f\xfe\x05\x00\x00\xff\xff\x2b\xa6\x15\x32\x40\x06\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1576, mode: os.FileMode(0644), modTime: time.Unix(1594803635, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xe, 0xc, 0xdf, 0xb6, 0xdb, 0x22, 0x1, 0x1f, 0xe0, 0x18, 0x96, 0x1b, 0x13, 0x5f, 0x60, 0x2c, 0x41, 0xd5, 0x4, 0x77, 0x99, 0x8a, 0x4d, 0xa1, 0xd6, 0x41, 0xb5, 0x1, 0x86, 0x29, 0x1e}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1600, mode: os.FileMode(0644), modTime: time.Unix(1594978360, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x57, 0x6b, 0xc6, 0x63, 0x1c, 0x3a, 0x78, 0xa0, 0xc3, 0x12, 0x63, 0xbf, 0x34, 0x6c, 0x86, 0xd2, 0xce, 0x6c, 0xfb, 0xdd, 0xa8, 0x44, 0x5c, 0x0, 0x4e, 0x1a, 0x99, 0xa1, 0xfc, 0xf5, 0x3b}} return a, nil } @@ -126,7 +126,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index 86be44f03..f970d4ef0 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS push_notification_client_info ( installation_id TEXT NOT NULL, access_token TEXT NOT NULL, retrieved_at INT NOT NULL, + version INT NOT NULL, UNIQUE(public_key, installation_id, server_public_key) ON CONFLICT REPLACE ); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 10a800f95..f7ecef168 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -145,7 +145,24 @@ func (p *Persistence) SavePushNotificationInfo(infos []*PushNotificationInfo) er _ = tx.Rollback() }() for _, info := range infos { - _, err := tx.Exec(`INSERT INTO push_notification_client_info (public_key, server_public_key, installation_id, access_token, retrieved_at) VALUES (?, ?, ?, ?, ?)`, crypto.CompressPubkey(info.PublicKey), crypto.CompressPubkey(info.ServerPublicKey), info.InstallationID, info.AccessToken, info.RetrievedAt) + var latestVersion uint64 + clientCompressedKey := crypto.CompressPubkey(info.PublicKey) + err := tx.QueryRow(`SELECT IFNULL(MAX(version),0) FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? LIMIT 1`, clientCompressedKey, info.InstallationID).Scan(&latestVersion) + if err != sql.ErrNoRows && err != nil { + return err + } + if latestVersion > info.Version { + // Nothing to do + continue + } + + // Remove anything that as a lower version + _, err = tx.Exec(`DELETE FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? AND version < ?`, clientCompressedKey, info.InstallationID, info.Version) + if err != nil { + return err + } + // Insert + _, err = tx.Exec(`INSERT INTO push_notification_client_info (public_key, server_public_key, installation_id, access_token, retrieved_at, version) VALUES (?, ?, ?, ?, ?,?)`, clientCompressedKey, crypto.CompressPubkey(info.ServerPublicKey), info.InstallationID, info.AccessToken, info.RetrievedAt, info.Version) if err != nil { return err } @@ -163,7 +180,7 @@ func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, instal inVector := strings.Repeat("?, ", len(installationIDs)-1) + "?" - rows, err := p.db.Query(`SELECT server_public_key, installation_id, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) + rows, err := p.db.Query(`SELECT server_public_key, installation_id, version, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) if err != nil { return nil, err } @@ -171,7 +188,7 @@ func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, instal for rows.Next() { var serverPublicKeyBytes []byte info := &PushNotificationInfo{PublicKey: publicKey} - err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.AccessToken, &info.RetrievedAt) + err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.Version, &info.AccessToken, &info.RetrievedAt) if err != nil { return nil, err } diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 0cf2947c3..05eb42e3f 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -94,6 +94,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key1.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID1, }, @@ -101,6 +102,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key1.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID2, }, @@ -108,6 +110,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key1.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID3, }, @@ -115,6 +118,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key2.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID1, }, @@ -122,6 +126,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key2.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID2, }, @@ -129,6 +134,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { PublicKey: &key2.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, + Version: 1, AccessToken: accessToken, InstallationID: installationID3, }, @@ -142,6 +148,69 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { s.Require().Len(retrievedInfos, 2) } +func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() { + installationID := "installation-id-1" + key, err := crypto.GenerateKey() + s.Require().NoError(err) + serverKey1, err := crypto.GenerateKey() + s.Require().NoError(err) + serverKey2, err := crypto.GenerateKey() + s.Require().NoError(err) + + accessToken := "token" + + infos := []*PushNotificationInfo{ + { + PublicKey: &key.PublicKey, + ServerPublicKey: &serverKey1.PublicKey, + RetrievedAt: 1, + Version: 1, + AccessToken: accessToken, + InstallationID: installationID, + }, + { + PublicKey: &key.PublicKey, + ServerPublicKey: &serverKey2.PublicKey, + RetrievedAt: 1, + Version: 1, + AccessToken: accessToken, + InstallationID: installationID, + }, + } + + s.Require().NoError(s.persistence.SavePushNotificationInfo(infos)) + + retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID}) + s.Require().NoError(err) + + // We should retrieve both + s.Require().Len(retrievedInfos, 2) + s.Require().Equal(uint64(1), retrievedInfos[0].Version) + + // Bump version + infos[0].Version = 2 + + s.Require().NoError(s.persistence.SavePushNotificationInfo(infos)) + + retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID}) + s.Require().NoError(err) + + // Only one should be retrieved now + s.Require().Len(retrievedInfos, 1) + s.Require().Equal(uint64(2), retrievedInfos[0].Version) + + // Lower version + infos[0].Version = 1 + + s.Require().NoError(s.persistence.SavePushNotificationInfo(infos)) + + retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID}) + s.Require().NoError(err) + + s.Require().Len(retrievedInfos, 1) + s.Require().Equal(uint64(2), retrievedInfos[0].Version) +} + func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() { // Try with nil first retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration() diff --git a/protocol/push_notification_server/migrations/migrations.go b/protocol/push_notification_server/migrations/migrations.go index 6e4ecec3f..c044d1bf6 100644 --- a/protocol/push_notification_server/migrations/migrations.go +++ b/protocol/push_notification_server/migrations/migrations.go @@ -86,7 +86,7 @@ func _1593601728_initial_schemaDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}} return a, nil } @@ -106,7 +106,7 @@ func _1593601728_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1594636412, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1594896582, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}} return a, nil } @@ -126,7 +126,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594393629, 0)} + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} return a, nil } diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 171fcb5e5..67a3a32dc 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -157,6 +157,7 @@ func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue info := &protobuf.PushNotificationQueryInfo{ PublicKey: idAndResponse.ID, Grant: registration.Grant, + Version: registration.Version, InstallationId: registration.InstallationId, } diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 910794894..92e5dd4d5 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -26,8 +26,6 @@ func TestMessengerPushNotificationSuite(t *testing.T) { suite.Run(t, new(MessengerPushNotificationSuite)) } -// TODO: to test. Register -> stop server -> re-start -> is it loading the topics? - type MessengerPushNotificationSuite struct { suite.Suite m *Messenger // main instance of Messenger @@ -290,3 +288,180 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 2) } + +func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { + + bob1DeviceToken := "token-1" + bob2DeviceToken := "token-2" + + bob1 := s.m + bob2 := s.newMessengerWithKey(s.shh, s.m.identity) + server := s.newPushNotificationServer(s.shh) + alice := s.newMessenger(s.shh) + bobInstallationIDs := []string{bob1.installationID, bob2.installationID} + + // Register bob1 + err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) + + // Receive message, reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + // Check reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob1.RetrieveAll() + s.Require().NoError(err) + + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + registered, err := bob1.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bob1Servers, err := bob1.GetPushNotificationServers() + s.Require().NoError(err) + + // Register bob2 + err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) + s.Require().NoError(err) + + // Receive message, reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + // Check reply + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = bob2.RetrieveAll() + s.Require().NoError(err) + + err = tt.RetryWithBackOff(func() error { + registered, err := bob2.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bob2Servers, err := bob2.GetPushNotificationServers() + s.Require().NoError(err) + + err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) + s.Require().NoError(err) + + // Receive push notification query + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = server.RetrieveAll() + s.Require().NoError(err) + + // Receive push notification query response + // TODO: find a better way to handle this waiting + time.Sleep(500 * time.Millisecond) + _, err = alice.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = alice.RetrieveAll() + s.Require().NoError(err) + + time.Sleep(500 * time.Millisecond) + _, err = alice.RetrieveAll() + s.Require().NoError(err) + + // Here we should poll, as we don't know whether they are already there + + info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + s.Require().NoError(err) + // Check we have replies for both bob1 and bob2 + s.Require().NotNil(info) + s.Require().Len(info, 2) + + var bob1Info, bob2Info *push_notification_client.PushNotificationInfo + + if info[0].AccessToken == bob1Servers[0].AccessToken { + bob1Info = info[0] + bob2Info = info[1] + } else { + bob2Info = info[0] + bob1Info = info[1] + } + + s.Require().NotNil(bob1Info) + s.Require().Equal(bob1.installationID, bob1Info.InstallationID) + s.Require().Equal(bob1Info.AccessToken, bob1Servers[0].AccessToken, bob1Info.AccessToken) + s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) + + s.Require().NotNil(bob2Info) + s.Require().Equal(bob2.installationID, bob2Info.InstallationID) + s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken) + s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) + + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + alice.logger.Info("BOB KEY", zap.Any("key", bob1.identity.PublicKey)) + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 2) +} diff --git a/services/ext/api.go b/services/ext/api.go index 62c9181e8..366f91b96 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -406,12 +406,20 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error { // PushNotifications server func (api *PublicAPI) StartPushNotificationServer() error { - // update settings + err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled", true) + if err != nil { + return err + } + return api.service.messenger.StartPushNotificationServer() } func (api *PublicAPI) StopPushNotificationServer() error { - // update settings + err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled", false) + if err != nil { + return err + } + return api.service.messenger.StopPushNotificationServer() } @@ -422,6 +430,11 @@ func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceTo if err != nil { return err } + err = api.service.accountsDB.SaveSetting("notifications-enabled?", true) + if err != nil { + return err + } + return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken) } @@ -430,6 +443,11 @@ func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error if err != nil { return err } + err = api.service.accountsDB.SaveSetting("notifications-enabled?", false) + if err != nil { + return err + } + return api.service.messenger.UnregisterFromPushNotifications(ctx) } @@ -450,6 +468,22 @@ func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error { return api.service.messenger.EnableSendingPushNotifications() } +func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only", true) + if err != nil { + return err + } + return api.service.messenger.EnablePushNotificationsFromContactsOnly() +} + +func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Context) error { + err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only", false) + if err != nil { + return err + } + return api.service.messenger.DisablePushNotificationsFromContactsOnly() +} + func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) if err != nil { @@ -459,6 +493,15 @@ func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBy return api.service.messenger.AddPushNotificationServer(ctx, publicKey) } +func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { + publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) + if err != nil { + return err + } + + return api.service.messenger.RemovePushNotificationServer(ctx, publicKey) +} + func (api *PublicAPI) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { return api.service.messenger.GetPushNotificationServers() } diff --git a/services/ext/service.go b/services/ext/service.go index d08f0e8ee..86236adcb 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -469,7 +469,7 @@ func buildMessengerOptions( return nil, err } - if settings.PushNotificationServerEnabled { + if settings.PushNotificationsServerEnabled { config := &push_notification_server.Config{ Logger: logger, } From bec8fbb855b31d383f006ba50088161c15ccd23d Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Fri, 17 Jul 2020 14:29:51 +0200 Subject: [PATCH 36/46] Handle allowedContact lists --- protocol/messenger.go | 25 ++++--- protocol/push_notification_client/client.go | 75 ++++++++++++++++--- .../push_notification_client/client_test.go | 46 +++++++++++- 3 files changed, 124 insertions(+), 22 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 0ac1dfae1..182497222 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -3071,16 +3071,12 @@ func (m *Messenger) EnableSendingPushNotifications() error { return nil } -// RegisterForPushNotification register deviceToken with any push notification server enabled -func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { - if m.pushNotificationClient == nil { - return errors.New("push notification client not enabled") - } - +func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string) { var contactIDs []*ecdsa.PublicKey var mutedChatIDs []string m.mutex.Lock() + defer m.mutex.Unlock() for _, contact := range m.allContacts { if contact.IsAdded() { pk, err := contact.PublicKey() @@ -3099,7 +3095,16 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke } } - m.mutex.Unlock() + return contactIDs, mutedChatIDs +} + +// RegisterForPushNotification register deviceToken with any push notification server enabled +func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { + if m.pushNotificationClient == nil { + return errors.New("push notification client not enabled") + } + + contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) } @@ -3115,7 +3120,8 @@ func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { return errors.New("no push notification client") } - return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly() + contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() + return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) } func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { @@ -3123,7 +3129,8 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { return errors.New("no push notification client") } - return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly() + contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() + return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) } func (m *Messenger) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index cda929a05..46ee030a0 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -75,9 +75,9 @@ type Config struct { // RemoteNotificationsEnabled is whether we should register with a remote server for push notifications RemoteNotificationsEnabled bool - // AllowyFromContactsOnly indicates whether we should be receiving push notifications + // allowyFromContactsOnly indicates whether we should be receiving push notifications // only from contacts - AllowFromContactsOnly bool + allowFromContactsOnly bool // InstallationID is the installation-id for this device InstallationID string @@ -101,8 +101,8 @@ type Client struct { // AccessToken is the access token that is currently being used AccessToken string - // DeviceToken is the device token for this device - DeviceToken string + // deviceToken is the device token for this device + deviceToken string // randomReader only used for testing so we have deterministic encryption reader io.Reader @@ -156,6 +156,7 @@ func (c *Client) loadLastPushNotificationRegistration() error { } c.lastContactIDs = lastContactIDs c.lastPushNotificationRegistration = lastRegistration + c.deviceToken = lastRegistration.Token return nil } @@ -407,10 +408,30 @@ func (p *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, return encryptedToken, nil } -func (p *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([][]byte, error) { +func (p *Client) decryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { + sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( + ecies.ImportECDSAPublic(publicKey), + accessTokenKeyLength, + accessTokenKeyLength, + ) + if err != nil { + return nil, err + } + decryptedToken, err := common.Decrypt(token, sharedKey) + if err != nil { + return nil, err + } + return decryptedToken, nil +} + +func (c *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([][]byte, error) { + // If we allow everyone, don't set the list + if !c.config.allowFromContactsOnly { + return nil, nil + } var encryptedTokens [][]byte for _, publicKey := range contactIDs { - encryptedToken, err := p.encryptToken(publicKey, token) + encryptedToken, err := c.encryptToken(publicKey, token) if err != nil { return nil, err } @@ -449,7 +470,7 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu TokenType: c.config.TokenType, Version: c.getVersion(), InstallationId: c.config.InstallationID, - Token: c.DeviceToken, + Token: c.deviceToken, Enabled: c.config.RemoteNotificationsEnabled, BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), AllowedUserList: allowedUserList, @@ -672,7 +693,7 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut // stop registration loop c.stopRegistrationLoop() - c.DeviceToken = deviceToken + c.deviceToken = deviceToken registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) if err != nil { @@ -752,6 +773,18 @@ func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey * return nil } +func (c *Client) handleAllowedUserList(publicKey *ecdsa.PublicKey, allowedUserList [][]byte) string { + for _, encryptedToken := range allowedUserList { + token, err := c.decryptToken(publicKey, encryptedToken) + if err != nil { + c.config.Logger.Warn("could not decrypt token", zap.Error(err)) + continue + } + return string(token) + } + return "" +} + // HandlePushNotificationQueryResponse should update the data in the database for a given user func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { @@ -775,6 +808,18 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ continue } + accessToken := info.AccessToken + + if len(info.AllowedUserList) != 0 { + accessToken = c.handleAllowedUserList(publicKey, info.AllowedUserList) + + } + + if len(accessToken) == 0 { + c.config.Logger.Info("not in the allowed users list") + continue + } + // We check the user has allowed this server to store this particular // access token, otherwise anyone could reply with a fake token // and receive notifications for a user @@ -881,13 +926,19 @@ func (c *Client) DisableSending() { c.config.SendEnabled = false } -func (c *Client) EnablePushNotificationsFromContactsOnly() error { - c.config.AllowFromContactsOnly = true +func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.allowFromContactsOnly = true + if c.lastPushNotificationRegistration != nil { + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) + } return nil } -func (c *Client) DisablePushNotificationsFromContactsOnly() error { - c.config.AllowFromContactsOnly = false +func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.allowFromContactsOnly = false + if c.lastPushNotificationRegistration != nil { + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) + } return nil } diff --git a/protocol/push_notification_client/client_test.go b/protocol/push_notification_client/client_test.go index 0306fe044..56b60a70c 100644 --- a/protocol/push_notification_client/client_test.go +++ b/protocol/push_notification_client/client_test.go @@ -79,6 +79,49 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { // Get token expectedUUID := uuid.New().String() + // Reset random generator + uuid.SetRand(rand.New(rand.NewSource(seed))) + + s.client.deviceToken = myDeviceToken + // Set reader + s.client.reader = bytes.NewReader([]byte(expectedUUID)) + + options := &protobuf.PushNotificationRegistration{ + Version: 1, + AccessToken: expectedUUID, + Token: myDeviceToken, + InstallationId: s.installationID, + Enabled: true, + BlockedChatList: mutedChatListHashes, + } + + actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) + s.Require().NoError(err) + + s.Require().Equal(options, actualMessage) +} + +func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() { + myDeviceToken := "device-token" + mutedChatList := []string{"a", "b"} + + // build chat lish hashes + var mutedChatListHashes [][]byte + for _, chatID := range mutedChatList { + mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID))) + } + + contactKey, err := crypto.GenerateKey() + s.Require().NoError(err) + contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey} + + // Set random generator for uuid + var seed int64 = 1 + uuid.SetRand(rand.New(rand.NewSource(seed))) + + // Get token + expectedUUID := uuid.New().String() + // set up reader reader := bytes.NewReader([]byte(expectedUUID)) @@ -95,7 +138,8 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - s.client.DeviceToken = myDeviceToken + s.client.config.allowFromContactsOnly = true + s.client.deviceToken = myDeviceToken // Set reader s.client.reader = bytes.NewReader([]byte(expectedUUID)) From 58817aad5b006501e1d11e79c9efa2e70863acfa Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 10:06:24 +0200 Subject: [PATCH 37/46] Use separate flag for allowonlyfromcontacst --- protocol/messenger.go | 12 + protocol/protobuf/push_notifications.pb.go | 146 +++++----- protocol/protobuf/push_notifications.proto | 1 + protocol/push_notification_client/client.go | 19 +- protocol/push_notification_server/server.go | 2 +- .../push_notification_server/server_test.go | 15 +- protocol/push_notification_test.go | 255 ++++++------------ 7 files changed, 188 insertions(+), 262 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 182497222..1127b1e8c 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1978,6 +1978,18 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } // We continue in any case, no changes to messenger continue + case protobuf.PushNotificationResponse: + logger.Debug("Received PushNotificationResponse") + if m.pushNotificationClient == nil { + continue + } + logger.Debug("Handling PushNotificationResponse") + // TODO: Compare DST with Identity + if err := m.pushNotificationClient.HandlePushNotificationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationResponse)); err != nil { + logger.Warn("failed to handle PushNotificationResponse", zap.Error(err)) + } + // We continue in any case, no changes to messenger + continue case protobuf.PushNotificationQueryResponse: logger.Debug("Received PushNotificationQueryResponse") diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 3b2b03a57..9e062eef4 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -114,19 +114,20 @@ func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) { } type PushNotificationRegistration struct { - TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` - AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` - Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` - AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` - BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` - Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` - Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` + Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` + AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` + Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` + Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` + Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` + AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} } @@ -224,6 +225,13 @@ func (m *PushNotificationRegistration) GetGrant() []byte { return nil } +func (m *PushNotificationRegistration) GetAllowFromContactsOnly() bool { + if m != nil { + return m.AllowFromContactsOnly + } + return false +} + type PushNotificationRegistrationResponse struct { Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"` @@ -794,59 +802,61 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 858 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0x51, 0x6f, 0xe3, 0x44, - 0x10, 0x66, 0x9d, 0xb4, 0x89, 0xa7, 0xa1, 0xcd, 0xad, 0x7a, 0x3d, 0x73, 0xe2, 0x20, 0x67, 0x90, - 0x88, 0x0e, 0x29, 0x42, 0x45, 0x82, 0x13, 0x4f, 0x84, 0xd6, 0x2d, 0x56, 0x1b, 0x3b, 0x6c, 0x5c, - 0x4e, 0x48, 0x48, 0x96, 0x13, 0x6f, 0x5b, 0xab, 0x39, 0xdb, 0xec, 0xae, 0x8b, 0xf2, 0x80, 0xc4, - 0x2f, 0x40, 0xe2, 0x95, 0x5f, 0x81, 0xf8, 0x3b, 0xbc, 0xf1, 0x4b, 0x90, 0xd7, 0x76, 0x70, 0x1c, - 0x37, 0xcd, 0x03, 0x4f, 0xf1, 0xcc, 0xce, 0xcc, 0xee, 0x7c, 0xdf, 0xcc, 0x17, 0xd0, 0xe2, 0x84, - 0xdf, 0xba, 0x61, 0x24, 0x82, 0xeb, 0x60, 0xe6, 0x89, 0x20, 0x0a, 0xf9, 0x20, 0x66, 0x91, 0x88, - 0x70, 0x5b, 0xfe, 0x4c, 0x93, 0x6b, 0xfd, 0xef, 0x06, 0xbc, 0x3f, 0x4e, 0xf8, 0xad, 0x55, 0x8a, - 0x22, 0xf4, 0x26, 0xe0, 0x82, 0xc9, 0x6f, 0x6c, 0x03, 0x88, 0xe8, 0x8e, 0x86, 0xae, 0x58, 0xc4, - 0x54, 0x43, 0x3d, 0xd4, 0xdf, 0x3f, 0xfe, 0x6c, 0x50, 0xe4, 0x0f, 0x36, 0xe5, 0x0e, 0x9c, 0x34, - 0xd1, 0x59, 0xc4, 0x94, 0xa8, 0xa2, 0xf8, 0xc4, 0x87, 0xb0, 0x23, 0x0d, 0x4d, 0xe9, 0xa1, 0xbe, - 0x4a, 0x32, 0x03, 0x7f, 0x02, 0x07, 0x41, 0xc8, 0x85, 0x37, 0x9f, 0xcb, 0x54, 0x37, 0xf0, 0xb5, - 0x86, 0x3c, 0xdf, 0x2f, 0xbb, 0x4d, 0x1f, 0xbf, 0x84, 0x8e, 0x37, 0x9b, 0x51, 0xce, 0xdd, 0xac, - 0x4a, 0x53, 0x46, 0xed, 0x65, 0x3e, 0x79, 0x21, 0xd6, 0xa0, 0x45, 0x43, 0x6f, 0x3a, 0xa7, 0xbe, - 0xb6, 0xd3, 0x43, 0xfd, 0x36, 0x29, 0xcc, 0xf4, 0xe4, 0x9e, 0x32, 0x1e, 0x44, 0xa1, 0xb6, 0xdb, - 0x43, 0xfd, 0x26, 0x29, 0x4c, 0xfc, 0x0a, 0x9e, 0x78, 0xf3, 0x79, 0xf4, 0x33, 0xf5, 0xdd, 0x84, - 0x53, 0xe6, 0xce, 0x03, 0x2e, 0xb4, 0x56, 0xaf, 0xd1, 0xef, 0x90, 0x83, 0xfc, 0xe0, 0x8a, 0x53, - 0x76, 0x19, 0x70, 0x91, 0xc6, 0x4e, 0xe7, 0xd1, 0xec, 0x8e, 0xfa, 0xee, 0xec, 0xd6, 0x13, 0x59, - 0x6c, 0x3b, 0x8b, 0xcd, 0x0f, 0x4e, 0x6e, 0x3d, 0x21, 0x63, 0x3f, 0x00, 0x48, 0x42, 0x26, 0x41, - 0xa1, 0x4c, 0x53, 0xe5, 0x73, 0x4a, 0x9e, 0x14, 0x8d, 0x1b, 0xe6, 0x85, 0x42, 0x83, 0x1e, 0xea, - 0x77, 0x48, 0x66, 0xe8, 0x67, 0xa0, 0x2e, 0xb1, 0xc3, 0x47, 0x80, 0xaf, 0xac, 0x0b, 0xcb, 0x7e, - 0x63, 0xb9, 0x8e, 0x7d, 0x61, 0x58, 0xae, 0xf3, 0xc3, 0xd8, 0xe8, 0xbe, 0x83, 0xdf, 0x05, 0x75, - 0x38, 0xce, 0x7d, 0x5d, 0x84, 0x31, 0xec, 0x9f, 0x99, 0xc4, 0xf8, 0x66, 0x38, 0x31, 0x72, 0x9f, - 0xa2, 0xff, 0xa5, 0xc0, 0xc7, 0x9b, 0x18, 0x22, 0x94, 0xc7, 0x51, 0xc8, 0x69, 0x0a, 0x0c, 0x4f, - 0x24, 0x84, 0x92, 0xe2, 0x36, 0x29, 0x4c, 0x6c, 0xc1, 0x0e, 0x65, 0x2c, 0x62, 0x92, 0xae, 0xfd, - 0xe3, 0xd7, 0xdb, 0x51, 0x5f, 0x14, 0x1e, 0x18, 0x69, 0xae, 0x1c, 0x81, 0xac, 0x0c, 0x7e, 0x01, - 0xc0, 0xe8, 0x4f, 0x09, 0xe5, 0xa2, 0xe0, 0xb8, 0x43, 0xd4, 0xdc, 0x63, 0xfa, 0xfa, 0xaf, 0x08, - 0xd4, 0x65, 0x4e, 0xb9, 0x75, 0x83, 0x10, 0x9b, 0x14, 0xad, 0x3f, 0x85, 0x27, 0xa3, 0xe1, 0xe5, - 0x99, 0x4d, 0x46, 0xc6, 0xa9, 0x3b, 0x32, 0x26, 0x93, 0xe1, 0xb9, 0xd1, 0x45, 0xf8, 0x10, 0xba, - 0xdf, 0x1b, 0x64, 0x62, 0xda, 0x96, 0x3b, 0x32, 0x27, 0xa3, 0xa1, 0x73, 0xf2, 0x6d, 0x57, 0xc1, - 0xcf, 0xe1, 0xe8, 0xca, 0x9a, 0x5c, 0x8d, 0xc7, 0x36, 0x71, 0x8c, 0xd3, 0x32, 0x86, 0x8d, 0x14, - 0x34, 0xd3, 0x72, 0x0c, 0x62, 0x0d, 0x2f, 0xb3, 0x1b, 0xba, 0x4d, 0xfd, 0x37, 0x04, 0x2f, 0xab, - 0xbd, 0x0d, 0xfd, 0x7b, 0xca, 0x44, 0xc0, 0xe9, 0x5b, 0x1a, 0x0a, 0x33, 0xbc, 0x8e, 0xd2, 0x3e, - 0xe2, 0x64, 0x3a, 0x0f, 0x66, 0xee, 0x1d, 0x5d, 0x48, 0xd0, 0x3a, 0x44, 0xcd, 0x3c, 0x17, 0x74, - 0xb1, 0x36, 0xa6, 0xca, 0xfa, 0x98, 0x6e, 0x3b, 0xf2, 0xfa, 0x2f, 0xa0, 0x9d, 0x44, 0xa1, 0xf0, - 0x66, 0xe2, 0x24, 0xf2, 0xe9, 0xca, 0x53, 0xb0, 0x07, 0x47, 0x6b, 0x5b, 0xee, 0x06, 0xe1, 0x75, - 0xa4, 0xa1, 0x5e, 0xa3, 0xbf, 0x77, 0xfc, 0xe9, 0xc3, 0x7c, 0xad, 0xf5, 0x44, 0x0e, 0xe3, 0x4a, - 0x48, 0xea, 0xd5, 0x5f, 0xc3, 0xd3, 0x6a, 0xea, 0x77, 0x09, 0x65, 0x0b, 0xfc, 0x21, 0xec, 0xfd, - 0x07, 0x01, 0x97, 0x17, 0x76, 0x08, 0x2c, 0x31, 0xe0, 0xfa, 0x3f, 0x08, 0xde, 0xab, 0x4d, 0x95, - 0x08, 0x56, 0x21, 0x42, 0x5b, 0x41, 0xa4, 0xd4, 0xaa, 0xc2, 0x2a, 0x1b, 0x8d, 0x2a, 0x1b, 0xb5, - 0xdb, 0xdd, 0xac, 0xdf, 0xee, 0xe5, 0x46, 0xee, 0x94, 0x36, 0xf2, 0x61, 0xe5, 0xd0, 0x7f, 0x47, - 0xf0, 0xa2, 0xb6, 0xc9, 0xe5, 0x72, 0x7d, 0x09, 0xcd, 0x12, 0x23, 0x1f, 0x3d, 0xcc, 0xc8, 0x12, - 0x1b, 0x22, 0x13, 0xd2, 0xae, 0xde, 0x52, 0xce, 0xbd, 0x1b, 0x5a, 0x74, 0xde, 0x21, 0x6a, 0xee, - 0x31, 0xfd, 0xf2, 0xd2, 0x36, 0x56, 0x96, 0x56, 0xff, 0x13, 0x41, 0xb7, 0x5a, 0x7c, 0x1b, 0xbc, - 0x9f, 0x41, 0x4b, 0x2a, 0xda, 0x12, 0xe7, 0xdd, 0xd4, 0x7c, 0x1c, 0xdf, 0x1a, 0x9e, 0x9a, 0xb5, - 0x3c, 0x69, 0xd0, 0xca, 0xdf, 0x9f, 0xc3, 0x5b, 0x98, 0x7a, 0x0c, 0xcf, 0xd6, 0x05, 0x45, 0xaa, - 0x02, 0xfe, 0x02, 0xda, 0xb9, 0x40, 0xf0, 0x1c, 0xc3, 0xe7, 0x1b, 0x54, 0x68, 0x19, 0xfb, 0x08, - 0x7c, 0xfa, 0x1f, 0x0a, 0x1c, 0xad, 0x5f, 0x19, 0x47, 0x4c, 0x6c, 0x90, 0xc3, 0xaf, 0x57, 0xe5, - 0xf0, 0xd5, 0x26, 0x39, 0x4c, 0x4b, 0xd5, 0x0a, 0xe0, 0xff, 0x01, 0xa5, 0xfe, 0xe3, 0x36, 0x42, - 0x79, 0x00, 0x7b, 0x6f, 0x88, 0x6d, 0x9d, 0x97, 0xff, 0x25, 0x2a, 0x82, 0xa7, 0xa4, 0x3e, 0xcb, - 0x76, 0x5c, 0x62, 0x9c, 0x9b, 0x13, 0xc7, 0x20, 0xc6, 0x69, 0xb7, 0xa1, 0x27, 0xa0, 0xad, 0x37, - 0x94, 0xcf, 0xf3, 0x2a, 0xae, 0xa8, 0x3a, 0x96, 0x5f, 0x41, 0x8b, 0xc9, 0xde, 0xb9, 0xa6, 0x48, - 0xb6, 0x7a, 0x8f, 0x81, 0x44, 0x8a, 0x84, 0xe9, 0xae, 0x8c, 0xfc, 0xfc, 0xdf, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x04, 0xbd, 0xc3, 0x50, 0xbb, 0x08, 0x00, 0x00, + // 891 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0xc7, 0xb1, 0x93, 0xb6, 0xf1, 0x49, 0x68, 0xb3, 0xa3, 0xb6, 0x6b, 0x56, 0x2c, 0x64, 0x0d, + 0x12, 0xd1, 0x22, 0x45, 0xa8, 0x48, 0xec, 0x8a, 0x2b, 0x42, 0xeb, 0x16, 0xab, 0x8d, 0x1d, 0x26, + 0x2e, 0x2b, 0x24, 0x24, 0xcb, 0x89, 0x27, 0xad, 0x55, 0xd7, 0x63, 0x66, 0xc6, 0x8b, 0x72, 0x81, + 0xc4, 0x13, 0x20, 0x21, 0x71, 0xc5, 0x53, 0x20, 0x1e, 0x89, 0x27, 0x41, 0x1e, 0xdb, 0xc1, 0xf9, + 0x68, 0x9a, 0x8b, 0xbd, 0x8a, 0xcf, 0x99, 0x73, 0x66, 0xe6, 0xfc, 0xce, 0x99, 0x7f, 0x40, 0x4f, + 0x52, 0x7e, 0xeb, 0xc5, 0x54, 0x84, 0xd3, 0x70, 0xe2, 0x8b, 0x90, 0xc6, 0xbc, 0x97, 0x30, 0x2a, + 0x28, 0x6a, 0xc8, 0x9f, 0x71, 0x3a, 0x35, 0xfe, 0xac, 0xc3, 0x87, 0xc3, 0x94, 0xdf, 0xda, 0x95, + 0x28, 0x4c, 0x6e, 0x42, 0x2e, 0x98, 0xfc, 0x46, 0x0e, 0x80, 0xa0, 0x77, 0x24, 0xf6, 0xc4, 0x2c, + 0x21, 0xba, 0xd2, 0x51, 0xba, 0xfb, 0x27, 0x5f, 0xf4, 0xca, 0xfc, 0xde, 0xa6, 0xdc, 0x9e, 0x9b, + 0x25, 0xba, 0xb3, 0x84, 0x60, 0x4d, 0x94, 0x9f, 0xe8, 0x10, 0x76, 0xa4, 0xa1, 0xab, 0x1d, 0xa5, + 0xab, 0xe1, 0xdc, 0x40, 0x9f, 0xc1, 0x41, 0x18, 0x73, 0xe1, 0x47, 0x91, 0x4c, 0xf5, 0xc2, 0x40, + 0xaf, 0xc9, 0xf5, 0xfd, 0xaa, 0xdb, 0x0a, 0xd0, 0x0b, 0x68, 0xf9, 0x93, 0x09, 0xe1, 0xdc, 0xcb, + 0x77, 0xa9, 0xcb, 0xa8, 0x66, 0xee, 0x93, 0x07, 0x22, 0x1d, 0xf6, 0x48, 0xec, 0x8f, 0x23, 0x12, + 0xe8, 0x3b, 0x1d, 0xa5, 0xdb, 0xc0, 0xa5, 0x99, 0xad, 0xbc, 0x25, 0x8c, 0x87, 0x34, 0xd6, 0x77, + 0x3b, 0x4a, 0xb7, 0x8e, 0x4b, 0x13, 0xbd, 0x84, 0x27, 0x7e, 0x14, 0xd1, 0x5f, 0x48, 0xe0, 0xa5, + 0x9c, 0x30, 0x2f, 0x0a, 0xb9, 0xd0, 0xf7, 0x3a, 0xb5, 0x6e, 0x0b, 0x1f, 0x14, 0x0b, 0xd7, 0x9c, + 0xb0, 0xab, 0x90, 0x8b, 0x2c, 0x76, 0x1c, 0xd1, 0xc9, 0x1d, 0x09, 0xbc, 0xc9, 0xad, 0x2f, 0xf2, + 0xd8, 0x46, 0x1e, 0x5b, 0x2c, 0x9c, 0xde, 0xfa, 0x42, 0xc6, 0x7e, 0x04, 0x90, 0xc6, 0x4c, 0x42, + 0x21, 0x4c, 0xd7, 0xe4, 0x75, 0x2a, 0x9e, 0x8c, 0xc6, 0x0d, 0xf3, 0x63, 0xa1, 0x43, 0x47, 0xe9, + 0xb6, 0x70, 0x6e, 0xa0, 0x57, 0xa0, 0xcb, 0x43, 0xbd, 0x29, 0xa3, 0xf7, 0xde, 0x84, 0xc6, 0xc2, + 0x9f, 0x08, 0xee, 0xd1, 0x38, 0x9a, 0xe9, 0x4d, 0xb9, 0xc7, 0x91, 0x5c, 0x3f, 0x67, 0xf4, 0xfe, + 0xb4, 0x58, 0x75, 0xe2, 0x68, 0x66, 0x9c, 0x83, 0x36, 0x87, 0x8e, 0x8e, 0x01, 0x5d, 0xdb, 0x97, + 0xb6, 0xf3, 0xc6, 0xf6, 0x5c, 0xe7, 0xd2, 0xb4, 0x3d, 0xf7, 0xc7, 0xa1, 0xd9, 0x7e, 0x0f, 0xbd, + 0x0f, 0x5a, 0x7f, 0x58, 0xf8, 0xda, 0x0a, 0x42, 0xb0, 0x7f, 0x6e, 0x61, 0xf3, 0xdb, 0xfe, 0xc8, + 0x2c, 0x7c, 0xaa, 0xf1, 0x8f, 0x0a, 0x9f, 0x6e, 0x6a, 0x2d, 0x26, 0x3c, 0xa1, 0x31, 0x27, 0x19, + 0x51, 0x9e, 0x4a, 0xf6, 0x72, 0x36, 0x1a, 0xb8, 0x34, 0x91, 0x0d, 0x3b, 0x84, 0x31, 0xca, 0x64, + 0x9f, 0xf7, 0x4f, 0x5e, 0x6f, 0x37, 0x33, 0xe5, 0xc6, 0x3d, 0x33, 0xcb, 0x95, 0xb3, 0x93, 0x6f, + 0x83, 0x9e, 0x03, 0x30, 0xf2, 0x73, 0x4a, 0xb8, 0x28, 0x87, 0xa3, 0x85, 0xb5, 0xc2, 0x63, 0x05, + 0xc6, 0x6f, 0x0a, 0x68, 0xf3, 0x9c, 0x6a, 0xe9, 0x26, 0xc6, 0x0e, 0x2e, 0x4b, 0x3f, 0x82, 0x27, + 0x83, 0xfe, 0xd5, 0xb9, 0x83, 0x07, 0xe6, 0x99, 0x37, 0x30, 0x47, 0xa3, 0xfe, 0x85, 0xd9, 0x56, + 0xd0, 0x21, 0xb4, 0x7f, 0x30, 0xf1, 0xc8, 0x72, 0x6c, 0x6f, 0x60, 0x8d, 0x06, 0x7d, 0xf7, 0xf4, + 0xbb, 0xb6, 0x8a, 0x9e, 0xc1, 0xf1, 0xb5, 0x3d, 0xba, 0x1e, 0x0e, 0x1d, 0xec, 0x9a, 0x67, 0x55, + 0x86, 0xb5, 0x0c, 0x9a, 0x65, 0xbb, 0x26, 0xb6, 0xfb, 0x57, 0xf9, 0x09, 0xed, 0xba, 0xf1, 0xbb, + 0x02, 0x2f, 0x96, 0x6b, 0xeb, 0x07, 0x6f, 0x09, 0x13, 0x21, 0x27, 0xf7, 0x24, 0x16, 0x56, 0x3c, + 0xa5, 0x59, 0x1d, 0x49, 0x3a, 0x8e, 0xc2, 0x89, 0x77, 0x47, 0x66, 0x12, 0x5a, 0x0b, 0x6b, 0xb9, + 0xe7, 0x92, 0xcc, 0x56, 0xe6, 0x5b, 0x5d, 0x9d, 0xef, 0x6d, 0xdf, 0x8a, 0xf1, 0x2b, 0xe8, 0xc5, + 0x74, 0x9c, 0xd2, 0x80, 0x2c, 0x5c, 0x05, 0xf9, 0x70, 0xbc, 0x22, 0x0f, 0x5e, 0x18, 0x4f, 0xa9, + 0xae, 0x74, 0x6a, 0xdd, 0xe6, 0xc9, 0xe7, 0x0f, 0xf7, 0x6b, 0xa5, 0x26, 0x7c, 0x98, 0x2c, 0x85, + 0x64, 0x5e, 0xe3, 0x35, 0x1c, 0x2d, 0xa7, 0x7e, 0x9f, 0x12, 0x36, 0x43, 0x1f, 0x43, 0xf3, 0x7f, + 0x04, 0x5c, 0x1e, 0xd8, 0xc2, 0x30, 0x67, 0xc0, 0x8d, 0x7f, 0x15, 0xf8, 0x60, 0x6d, 0xaa, 0x24, + 0xb8, 0x8c, 0x48, 0xd9, 0x0a, 0x91, 0xba, 0x56, 0x4e, 0x16, 0xbb, 0x51, 0x5b, 0xee, 0xc6, 0x5a, + 0x59, 0xa8, 0xaf, 0x97, 0x85, 0xf9, 0x53, 0xde, 0xa9, 0x3e, 0xe5, 0x07, 0x25, 0xc7, 0xf8, 0x43, + 0x81, 0xe7, 0x6b, 0x8b, 0x9c, 0x3f, 0xae, 0x57, 0x50, 0xaf, 0x74, 0xe4, 0x93, 0x87, 0x3b, 0x32, + 0x67, 0x83, 0x65, 0x42, 0x56, 0xd5, 0x3d, 0xe1, 0xdc, 0xbf, 0x21, 0x65, 0xe5, 0x2d, 0xac, 0x15, + 0x1e, 0x2b, 0xa8, 0x3e, 0xda, 0xda, 0xc2, 0xa3, 0x35, 0xfe, 0x56, 0xa0, 0xbd, 0xbc, 0xf9, 0x36, + 0xbc, 0x9f, 0xc2, 0x9e, 0x94, 0xc2, 0x39, 0xe7, 0xdd, 0xcc, 0x7c, 0x9c, 0xef, 0x9a, 0x3e, 0xd5, + 0xd7, 0xf6, 0x49, 0x87, 0xbd, 0xe2, 0xfe, 0x05, 0xde, 0xd2, 0x34, 0x12, 0x78, 0xba, 0x2a, 0x28, + 0x52, 0x15, 0xd0, 0x57, 0xd0, 0x28, 0x04, 0x82, 0x17, 0x0c, 0x9f, 0x6d, 0x50, 0xa1, 0x79, 0xec, + 0x23, 0xf8, 0x8c, 0xbf, 0x54, 0x38, 0x5e, 0x3d, 0x32, 0xa1, 0x4c, 0x6c, 0x90, 0xc3, 0x6f, 0x16, + 0xe5, 0xf0, 0xe5, 0x26, 0x39, 0xcc, 0xb6, 0x5a, 0x2b, 0x80, 0xef, 0x02, 0xa5, 0xf1, 0xd3, 0x36, + 0x42, 0x79, 0x00, 0xcd, 0x37, 0xd8, 0xb1, 0x2f, 0xaa, 0xff, 0x12, 0x4b, 0x82, 0xa7, 0x66, 0x3e, + 0xdb, 0x71, 0x3d, 0x6c, 0x5e, 0x58, 0x23, 0xd7, 0xc4, 0xe6, 0x59, 0xbb, 0x66, 0xa4, 0xa0, 0xaf, + 0x16, 0x54, 0xcc, 0xf3, 0x22, 0x57, 0x65, 0x79, 0x2c, 0xbf, 0x86, 0x3d, 0x26, 0x6b, 0xe7, 0xba, + 0x2a, 0xbb, 0xd5, 0x79, 0x0c, 0x12, 0x2e, 0x13, 0xc6, 0xbb, 0x32, 0xf2, 0xcb, 0xff, 0x02, 0x00, + 0x00, 0xff, 0xff, 0x90, 0x90, 0x1b, 0xe9, 0xf4, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index 612dfc678..6642c0bc2 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -18,6 +18,7 @@ message PushNotificationRegistration { repeated bytes blocked_chat_list = 8; bool unregister = 9; bytes grant = 10; + bool allow_from_contacts_only = 11; } message PushNotificationRegistrationResponse { diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 46ee030a0..c5c51246a 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -466,14 +466,15 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu } options := &protobuf.PushNotificationRegistration{ - AccessToken: token, - TokenType: c.config.TokenType, - Version: c.getVersion(), - InstallationId: c.config.InstallationID, - Token: c.deviceToken, - Enabled: c.config.RemoteNotificationsEnabled, - BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), - AllowedUserList: allowedUserList, + AccessToken: token, + TokenType: c.config.TokenType, + Version: c.getVersion(), + InstallationId: c.config.InstallationID, + Token: c.deviceToken, + AllowFromContactsOnly: c.config.allowFromContactsOnly, + Enabled: c.config.RemoteNotificationsEnabled, + BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), + AllowedUserList: allowedUserList, } return options, nil } @@ -847,7 +848,7 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ } // HandlePushNotificationResponse should set the request as processed -func (p *Client) HandlePushNotificationResponse(ack *protobuf.PushNotificationResponse) error { +func (p *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { return nil } diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 67a3a32dc..8442c5ada 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -161,7 +161,7 @@ func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue InstallationId: registration.InstallationId, } - if len(registration.AllowedUserList) > 0 { + if len(registration.AllowedUserList) > 0 && registration.AllowFromContactsOnly { info.AllowedUserList = registration.AllowedUserList } else { info.AccessToken = registration.AccessToken diff --git a/protocol/push_notification_server/server_test.go b/protocol/push_notification_server/server_test.go index e895af70f..f0e3fb1d5 100644 --- a/protocol/push_notification_server/server_test.go +++ b/protocol/push_notification_server/server_test.go @@ -549,13 +549,14 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { // Successful registration := &protobuf.PushNotificationRegistration{ - Token: "abc", - AccessToken: s.accessToken, - Grant: s.grant, - TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, - InstallationId: s.installationID, - AllowedUserList: allowedUserList, - Version: 1, + Token: "abc", + AccessToken: s.accessToken, + Grant: s.grant, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + InstallationId: s.installationID, + AllowFromContactsOnly: true, + AllowedUserList: allowedUserList, + Version: 1, } payload, err := proto.Marshal(registration) s.Require().NoError(err) diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 92e5dd4d5..0e4069920 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "os" "testing" - "time" "github.com/google/uuid" "github.com/stretchr/testify/suite" @@ -129,36 +128,16 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) - // Receive message, reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - // Check reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() - s.Require().NoError(err) - // Pull servers and check we registered err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob1.RetrieveAll() + if err != nil { + return err + } registered, err := bob1.RegisteredForPushNotifications() if err != nil { return err @@ -180,35 +159,16 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) s.Require().NoError(err) - // Receive message, reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - // Check reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob2.RetrieveAll() + if err != nil { + return err + } + registered, err := bob2.RegisteredForPushNotifications() if err != nil { return err @@ -226,42 +186,32 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) s.Require().NoError(err) - // Receive push notification query - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() + var info []*push_notification_client.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for both bob1 and bob2 + if len(info) != 2 { + return errors.New("info not fetched") + } + return nil + + }) + s.Require().NoError(err) - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - // Receive push notification query response - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - // Here we should poll, as we don't know whether they are already there - - info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - s.Require().NoError(err) // Check we have replies for both bob1 and bob2 - s.Require().NotNil(info) - s.Require().Len(info, 2) - var bob1Info, bob2Info *push_notification_client.PushNotificationInfo if info[0].AccessToken == bob1Servers[0].AccessToken { @@ -305,37 +255,18 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO s.Require().NoError(err) err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) - - // Receive message, reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - // Check reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob1.RetrieveAll() s.Require().NoError(err) // Pull servers and check we registered err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob1.RetrieveAll() + if err != nil { + return err + } registered, err := bob1.RegisteredForPushNotifications() if err != nil { return err @@ -357,35 +288,17 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) s.Require().NoError(err) - // Receive message, reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) - - // Check reply - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = bob2.RetrieveAll() - s.Require().NoError(err) - err = tt.RetryWithBackOff(func() error { + // Fetch server messages, for the registration + _, err = server.RetrieveAll() + if err != nil { + return err + } + // Fetch bob messages, for the response + _, err = bob2.RetrieveAll() + if err != nil { + return err + } registered, err := bob2.RegisteredForPushNotifications() if err != nil { return err @@ -403,42 +316,30 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) s.Require().NoError(err) - // Receive push notification query - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) + var info []*push_notification_client.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() - s.Require().NoError(err) + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for both bob1 and bob2 + if len(info) != 2 { + return errors.New("info not fetched") + } + return nil - time.Sleep(500 * time.Millisecond) - _, err = server.RetrieveAll() + }) s.Require().NoError(err) - // Receive push notification query response - // TODO: find a better way to handle this waiting - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - time.Sleep(500 * time.Millisecond) - _, err = alice.RetrieveAll() - s.Require().NoError(err) - - // Here we should poll, as we don't know whether they are already there - - info, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - s.Require().NoError(err) - // Check we have replies for both bob1 and bob2 - s.Require().NotNil(info) - s.Require().Len(info, 2) - var bob1Info, bob2Info *push_notification_client.PushNotificationInfo if info[0].AccessToken == bob1Servers[0].AccessToken { From 52e7089e39a0d53fc1f9f5a0264df582edee2d75 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 10:32:00 +0200 Subject: [PATCH 38/46] Test contact only notifications --- protocol/push_notification_client/client.go | 8 +- protocol/push_notification_server/server.go | 2 +- protocol/push_notification_test.go | 103 +++++++------------- 3 files changed, 40 insertions(+), 73 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index c5c51246a..0e9580434 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -775,12 +775,14 @@ func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey * } func (c *Client) handleAllowedUserList(publicKey *ecdsa.PublicKey, allowedUserList [][]byte) string { + c.config.Logger.Info("handling allowed user list") for _, encryptedToken := range allowedUserList { token, err := c.decryptToken(publicKey, encryptedToken) if err != nil { c.config.Logger.Warn("could not decrypt token", zap.Error(err)) continue } + c.config.Logger.Info("decrypted token") return string(token) } return "" @@ -811,7 +813,7 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ accessToken := info.AccessToken - if len(info.AllowedUserList) != 0 { + if len(accessToken) == 0 && len(info.AllowedUserList) != 0 { accessToken = c.handleAllowedUserList(publicKey, info.AllowedUserList) } @@ -824,14 +826,14 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ // We check the user has allowed this server to store this particular // access token, otherwise anyone could reply with a fake token // and receive notifications for a user - if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, info.AccessToken); err != nil { + if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, accessToken); err != nil { c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err)) continue } pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ PublicKey: publicKey, ServerPublicKey: serverPublicKey, - AccessToken: info.AccessToken, + AccessToken: accessToken, InstallationID: info.InstallationId, Version: info.Version, RetrievedAt: time.Now().Unix(), diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 8442c5ada..9ec2fc12f 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -161,7 +161,7 @@ func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue InstallationId: registration.InstallationId, } - if len(registration.AllowedUserList) > 0 && registration.AllowFromContactsOnly { + if registration.AllowFromContactsOnly { info.AllowedUserList = registration.AllowedUserList } else { info.AccessToken = registration.AccessToken diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 0e4069920..ddac6e8fe 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -224,7 +224,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NotNil(bob1Info) s.Require().Equal(bob1.installationID, bob1Info.InstallationID) - s.Require().Equal(bob1Info.AccessToken, bob1Servers[0].AccessToken, bob1Info.AccessToken) + s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken) s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) s.Require().NotNil(bob2Info) @@ -233,7 +233,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - alice.logger.Info("BOB KEY", zap.Any("key", bob1.identity.PublicKey)) + s.Require().NoError(err) s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 2) @@ -241,20 +241,33 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { - bob1DeviceToken := "token-1" - bob2DeviceToken := "token-2" + bobDeviceToken := "token-1" - bob1 := s.m + bob := s.m bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) alice := s.newMessenger(s.shh) - bobInstallationIDs := []string{bob1.installationID, bob2.installationID} + bobInstallationIDs := []string{bob.installationID, bob2.installationID} - // Register bob1 - err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + // Register bob + err := bob.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) - err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) + // Add alice has a contact + aliceContact := &Contact{ + ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)), + Name: "Some Contact", + SystemTags: []string{contactAdded}, + } + + err = bob.SaveContact(aliceContact) + s.Require().NoError(err) + + // Enable from contacts only + err = bob.EnablePushNotificationsFromContactsOnly() + s.Require().NoError(err) + + err = bob.RegisterForPushNotifications(context.Background(), bobDeviceToken) s.Require().NoError(err) // Pull servers and check we registered @@ -263,11 +276,11 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO if err != nil { return err } - _, err = bob1.RetrieveAll() + _, err = bob.RetrieveAll() if err != nil { return err } - registered, err := bob1.RegisteredForPushNotifications() + registered, err := bob.RegisteredForPushNotifications() if err != nil { return err } @@ -278,39 +291,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO }) // Make sure we receive it s.Require().NoError(err) - bob1Servers, err := bob1.GetPushNotificationServers() - s.Require().NoError(err) - - // Register bob2 - err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) - s.Require().NoError(err) - - err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) - s.Require().NoError(err) - - err = tt.RetryWithBackOff(func() error { - // Fetch server messages, for the registration - _, err = server.RetrieveAll() - if err != nil { - return err - } - // Fetch bob messages, for the response - _, err = bob2.RetrieveAll() - if err != nil { - return err - } - registered, err := bob2.RegisteredForPushNotifications() - if err != nil { - return err - } - if !registered { - return errors.New("not registered") - } - return nil - }) - // Make sure we receive it - s.Require().NoError(err) - bob2Servers, err := bob2.GetPushNotificationServers() + bobServers, err := bob.GetPushNotificationServers() s.Require().NoError(err) err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) @@ -327,12 +308,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO return err } - info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) if err != nil { return err } - // Check we have replies for both bob1 and bob2 - if len(info) != 2 { + // Check we have replies for bob + if len(info) != 1 { return errors.New("info not fetched") } return nil @@ -340,29 +321,13 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO }) s.Require().NoError(err) - var bob1Info, bob2Info *push_notification_client.PushNotificationInfo + s.Require().NotNil(info) + s.Require().Equal(bob.installationID, info[0].InstallationID) + s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken) + s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey) - if info[0].AccessToken == bob1Servers[0].AccessToken { - bob1Info = info[0] - bob2Info = info[1] - } else { - bob2Info = info[0] - bob1Info = info[1] - } - - s.Require().NotNil(bob1Info) - s.Require().Equal(bob1.installationID, bob1Info.InstallationID) - s.Require().Equal(bob1Info.AccessToken, bob1Servers[0].AccessToken, bob1Info.AccessToken) - s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) - - s.Require().NotNil(bob2Info) - s.Require().Equal(bob2.installationID, bob2Info.InstallationID) - s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken) - s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) - - retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) - alice.logger.Info("BOB KEY", zap.Any("key", bob1.identity.PublicKey)) + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) s.Require().NoError(err) s.Require().NotNil(retrievedNotificationInfo) - s.Require().Len(retrievedNotificationInfo, 2) + s.Require().Len(retrievedNotificationInfo, 1) } From aa5aa633422fd93b1650f003293d883ef9bdca33 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 12:01:42 +0200 Subject: [PATCH 39/46] Save response from push notification --- protocol/push_notification_client/client.go | 83 +++++++++++++------ .../push_notification_client/client_test.go | 15 ++-- .../migrations/migrations.go | 8 +- .../sql/1593601729_initial_schema.up.sql | 3 + .../push_notification_client/persistence.go | 35 ++++++-- .../persistence_test.go | 69 +++++++++++++++ 6 files changed, 171 insertions(+), 42 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 0e9580434..59f48680d 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -67,6 +67,19 @@ type PushNotificationInfo struct { Version uint64 } +type SentNotification struct { + PublicKey *ecdsa.PublicKey + InstallationID string + SentAt int64 + MessageID []byte + Success bool + Error protobuf.PushNotificationReport_ErrorType +} + +func (s *SentNotification) HashedPublicKey() []byte { + return common.HashPublicKey(s.PublicKey) +} + type Config struct { // Identity is our identity key Identity *ecdsa.PrivateKey @@ -197,6 +210,35 @@ func (c *Client) Stop() error { return nil } +func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey) error { + // Check if we queried recently + queriedAt, err := c.persistence.GetQueriedAt(publicKey) + if err != nil { + return err + } + // Naively query again if too much time has passed. + // Here it might not be necessary + if time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { + c.config.Logger.Info("querying info") + err := c.QueryPushNotificationInfo(publicKey) + if err != nil { + c.config.Logger.Error("could not query pn info", zap.Error(err)) + return err + } + // This is just horrible, but for now will do, + // the issue is that we don't really know how long it will + // take to reply, as there might be multiple servers + // replying to us. + // The only time we are 100% certain that we can proceed is + // when we have non-stale info for each device, but + // most devices are not going to be registered, so we'd still + // have to wait teh maximum amount of time allowed. + time.Sleep(3 * time.Second) + + } + return nil +} + func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { c.config.Logger.Info("sent message", zap.Any("sent message", sentMessage)) if !c.config.SendEnabled { @@ -252,33 +294,10 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { c.config.Logger.Info("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) - // Check if we queried recently - queriedAt, err := c.persistence.GetQueriedAt(publicKey) + err := c.queryNotificationInfo(publicKey) if err != nil { return err } - - // Naively query again if too much time has passed. - // Here it might not be necessary - if time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { - c.config.Logger.Info("querying info") - err := c.QueryPushNotificationInfo(publicKey) - if err != nil { - c.config.Logger.Error("could not query pn info", zap.Error(err)) - return err - } - // This is just horrible, but for now will do, - // the issue is that we don't really know how long it will - // take to reply, as there might be multiple servers - // replying to us. - // The only time we are 100% certain that we can proceed is - // when we have non-stale info for each device, but - // most devices are not going to be registered, so we'd still - // have to wait teh maximum amount of time allowed. - time.Sleep(3 * time.Second) - - } - c.config.Logger.Info("queried info") // Retrieve infos info, err := c.GetPushNotificationInfo(publicKey, installationIDs) @@ -380,7 +399,12 @@ func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID strin } func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { - return c.persistence.NotifiedOn(publicKey, installationID, messageID) + return c.persistence.NotifiedOn(&SentNotification{ + PublicKey: publicKey, + SentAt: time.Now().Unix(), + InstallationID: installationID, + MessageID: messageID, + }) } func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte @@ -850,7 +874,14 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ } // HandlePushNotificationResponse should set the request as processed -func (p *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { +func (c *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { + messageID := response.MessageId + for _, report := range response.Reports { + err := c.persistence.UpdateNotificationResponse(messageID, report) + if err != nil { + return err + } + } return nil } diff --git a/protocol/push_notification_client/client_test.go b/protocol/push_notification_client/client_test.go index 56b60a70c..11b5aaa03 100644 --- a/protocol/push_notification_client/client_test.go +++ b/protocol/push_notification_client/client_test.go @@ -144,13 +144,14 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsO s.client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationRegistration{ - Version: 1, - AccessToken: expectedUUID, - Token: myDeviceToken, - InstallationId: s.installationID, - Enabled: true, - BlockedChatList: mutedChatListHashes, - AllowedUserList: [][]byte{encryptedToken}, + Version: 1, + AccessToken: expectedUUID, + Token: myDeviceToken, + InstallationId: s.installationID, + AllowFromContactsOnly: true, + Enabled: true, + BlockedChatList: mutedChatListHashes, + AllowedUserList: [][]byte{encryptedToken}, } actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index 35f6e5633..431630032 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.6kB) +// 1593601729_initial_schema.up.sql (1.709kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x94\xc1\x6e\xa3\x30\x10\x86\xef\x3c\xc5\x1c\x1b\x29\x87\xbd\xf7\x44\xa8\x59\x21\x59\xf6\x6e\x62\xa4\xdc\x2c\xaf\x71\x1b\x2b\xac\xe9\xda\xa6\x5a\xde\x7e\x65\x08\x29\xa9\x59\xa8\xda\x5e\x22\x65\xfc\x7b\xf0\x37\xf3\xcf\x64\x7b\x94\x32\x04\x2c\xdd\x61\x04\x45\x0e\x84\x32\x40\xc7\xe2\xc0\x0e\xf0\xdc\xba\x13\x37\x8d\xd7\x8f\x5a\x0a\xaf\x1b\xc3\x65\xad\x95\xf1\xdc\x29\xfb\xa2\xac\x83\xbb\x04\xe0\xb9\xfd\x55\x6b\xc9\xcf\xaa\x83\x1d\xa6\xbb\xfe\x3e\x29\x31\xde\x26\x00\x56\x3d\x69\xe7\x95\x55\x15\xec\x28\xc5\x28\x25\xf0\x80\xf2\xb4\xc4\x0c\xf2\x14\x1f\xd0\xad\x86\x0b\x0f\x05\x61\xd7\x0c\x57\xed\xb7\xa0\xab\x85\xf3\xdc\x2a\x6f\xf5\x9a\x32\x88\x3a\x2e\x9b\xd6\x2c\xa9\x84\x94\xca\x39\xee\x9b\xb3\x32\xc0\xd0\x91\x85\x60\x49\x8a\x9f\x25\xba\x7b\x65\xda\x00\x25\x90\x51\x92\xe3\x22\x63\xb0\x47\x3f\x70\x9a\xa1\x64\x73\x9f\x24\x1f\xa9\xdb\x9f\x56\x59\xad\xd6\xeb\x36\xe8\x22\xcc\xf1\xa8\xe3\xba\x8a\x2f\x45\x6f\xdf\x8e\xda\xaf\x85\xd0\xe6\xb1\x59\x25\x18\x1c\xc2\x97\x24\xda\x38\x2f\xea\x7a\xc8\xad\xab\xbe\x07\x37\x82\xa8\x43\x6f\xbc\x15\xac\xf0\x32\x5f\xa5\xe0\x4e\xdd\x98\x28\x1e\xd7\xe8\xed\x33\xb6\xf1\xd3\xbf\xb6\x7c\xde\x0a\x79\x56\x15\xff\xad\x9c\x13\x4f\x17\x33\x5c\xfe\xcc\xf6\x55\x9e\x84\x9f\xad\xcf\x98\x69\x86\xff\xc2\xf9\x9a\xf6\x96\xa1\xf8\x4e\xe8\x1e\x25\x00\x1f\x85\x70\xe1\x67\x7a\xb0\x8e\xf1\x29\x2b\xf4\xdf\x7b\x0f\xe7\x16\x16\x7a\xbb\xf9\x04\xf1\xb0\xa6\xec\x04\x76\x5c\x5d\x43\x2c\x86\x02\x90\x8d\xf1\x42\x86\xe6\xb9\xfe\x78\x88\xba\xce\xf8\x93\xf2\x5a\x06\xd2\xff\xef\xa7\x2b\xdc\x54\xbf\x6a\xc5\x82\x3c\xa0\x23\xe8\xea\x2f\x5f\x9c\xdf\xe9\x60\x52\xb2\x3c\xeb\x4b\xd3\xb2\xb9\x4f\xfe\x05\x00\x00\xff\xff\x2b\xa6\x15\x32\x40\x06\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x6e\xe2\x30\x10\xbd\xe7\x2b\xe6\x58\x24\x0e\x7b\xef\x29\x50\xb3\x8a\x64\x39\xbb\x60\x24\x6e\x96\xd7\x99\x36\x16\x59\xa7\x6b\x3b\xd5\xf2\xf7\x2b\x27\x90\x42\x9d\x75\xa4\x96\x0b\x12\x33\xcf\xa3\x79\x6f\x5e\xde\x7a\x4b\x72\x4e\x80\xe7\x2b\x4a\xa0\xd8\x00\x2b\x39\x90\x43\xb1\xe3\x3b\x78\xed\x5c\x2d\x4c\xeb\xf5\xb3\x56\xd2\xeb\xd6\x08\xd5\x68\x34\x5e\x38\xb4\x6f\x68\x1d\x3c\x64\x00\xaf\xdd\xaf\x46\x2b\x71\xc4\x13\xac\x68\xb9\xea\xdf\xb3\x3d\xa5\xcb\x0c\xc0\xe2\x8b\x76\x1e\x2d\x56\xb0\x2a\x4b\x4a\x72\x06\x4f\x64\x93\xef\x29\x87\x4d\x4e\x77\xe4\x16\x23\xa4\x87\x82\xf1\x71\xc2\x88\xfd\x16\x70\x8d\x74\x5e\x58\xf4\x56\xcf\x21\x03\xe8\x24\x54\xdb\x99\x14\x4a\x2a\x85\xce\x09\xdf\x1e\xd1\x00\x27\x07\x1e\x8a\x7b\x56\xfc\xdc\x93\x87\x77\x4e\x0b\x28\x19\xac\x4b\xb6\xa1\xc5\x9a\xc3\x96\xfc\xa0\xf9\x9a\x64\x8b\xc7\x2c\xfb\x8c\x6e\x7f\x3a\xb4\x1a\xe7\x75\x1b\x70\x11\xcd\x4b\xeb\x24\x74\x15\x3f\x8a\x76\x5f\x5e\xb0\xf7\x25\xa1\xcd\x73\x3b\xcb\x60\x70\x88\x48\x41\xb4\x71\x5e\x36\xcd\x30\x5b\x57\xfd\x0d\x6e\x00\xd1\x85\x3e\x78\x2b\x58\xe1\x6d\x5a\xa5\xe0\x4e\xdd\x9a\xa8\x1e\x6b\xf4\x71\x8d\x65\xbc\xfa\x7d\xe5\xf3\x56\xaa\x23\x56\xe2\x37\x3a\x27\x5f\xce\x66\x38\xff\x99\xbc\xab\xaa\xa5\x9f\xd4\xe7\x32\x69\x82\xff\x99\xe7\xfb\xd8\x5b\x0e\xc5\x77\x56\x6e\x49\x06\xf0\x59\x12\x2e\xfc\x5c\x37\xe6\x69\xa4\xac\x50\x4b\x57\x63\xf5\x35\xb7\xf4\x2b\x4d\x48\xe1\xba\xde\x46\x63\x00\x45\x69\x30\x26\x11\x5a\xdb\xda\x44\x62\x44\xa2\x2e\x21\x61\xa4\xc5\x17\xe4\x1d\x32\xd1\x5e\x29\x7b\xc9\xc9\xa1\x16\xcb\x03\xa0\x5a\xe3\xa5\x0a\x4e\x71\x7d\x7b\xa8\xba\x93\xf1\x35\x7a\xad\x82\x66\xff\xa7\x36\x92\xbb\xc6\xcf\xfa\xbe\x60\x4f\xe4\x00\xba\xfa\x2b\x92\x61\x71\x7d\xd7\x92\xa5\x83\x25\xf5\x69\x2e\x1e\xb3\x7f\x01\x00\x00\xff\xff\xed\x10\xc3\xcd\xad\x06\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1600, mode: os.FileMode(0644), modTime: time.Unix(1594978360, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd, 0x57, 0x6b, 0xc6, 0x63, 0x1c, 0x3a, 0x78, 0xa0, 0xc3, 0x12, 0x63, 0xbf, 0x34, 0x6c, 0x86, 0xd2, 0xce, 0x6c, 0xfb, 0xdd, 0xa8, 0x44, 0x5c, 0x0, 0x4e, 0x1a, 0x99, 0xa1, 0xfc, 0xf5, 0x3b}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1709, mode: os.FileMode(0644), modTime: time.Unix(1595237467, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x35, 0x40, 0x6a, 0x4a, 0x45, 0x37, 0x37, 0x99, 0x97, 0x5, 0xb3, 0x43, 0x6, 0x43, 0xcc, 0x10, 0x32, 0xbc, 0x16, 0xcc, 0xe0, 0xfb, 0x3, 0xa8, 0xce, 0x6a, 0x6b, 0x39, 0xd4, 0xe0, 0xbe, 0xa4}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index f970d4ef0..d4e4941e6 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -35,8 +35,11 @@ CREATE TABLE IF NOT EXISTS push_notification_client_tracked_messages ( CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( message_id BLOB NOT NULL, public_key BLOB NOT NULL, + hashed_public_key BLOB NOT NULL, installation_id TEXT NOT NULL, sent_at INT NOT NULL, + success BOOLEAN NOT NULL DEFAULT FALSE, + error INT NOT NULL DEFAULT 0, UNIQUE(message_id, public_key, installation_id) ); diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index f7ecef168..42fd128c9 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -42,7 +42,7 @@ func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotif return nil, nil, err } for _, pkBytes := range publicKeyBytes { - pk, err := crypto.UnmarshalPubkey(pkBytes) + pk, err := crypto.DecompressPubkey(pkBytes) if err != nil { return nil, nil, err } @@ -63,7 +63,7 @@ func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobu var encodedContactIDs bytes.Buffer var contactIDsBytes [][]byte for _, pk := range contactIDs { - contactIDsBytes = append(contactIDsBytes, crypto.FromECDSAPub(pk)) + contactIDsBytes = append(contactIDsBytes, crypto.CompressPubkey(pk)) } pkEncoder := gob.NewEncoder(&encodedContactIDs) if err := pkEncoder.Encode(contactIDsBytes); err != nil { @@ -271,9 +271,34 @@ func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecds return count == 0, nil } -func (p *Persistence) NotifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { - sentAt := time.Now().Unix() - _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, sent_at) VALUES (?, ?, ?, ?)`, crypto.CompressPubkey(publicKey), installationID, messageID, sentAt) +func (p *Persistence) NotifiedOn(n *SentNotification) error { + _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, sent_at, hashed_public_key) VALUES (?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.SentAt, n.HashedPublicKey()) + return err +} + +func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) { + var publicKeyBytes []byte + sentNotification := &SentNotification{ + InstallationID: installationID, + MessageID: messageID, + } + err := p.db.QueryRow(`SELECT sent_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.SentAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes) + if err != nil { + return nil, err + } + + publicKey, err := crypto.DecompressPubkey(publicKeyBytes) + if err != nil { + return nil, err + } + + sentNotification.PublicKey = publicKey + + return sentNotification, nil +} + +func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *protobuf.PushNotificationReport) error { + _, err := p.db.Exec(`UPDATE push_notification_client_sent_notifications SET success = ?, error = ? WHERE hashed_public_key = ? AND installation_id = ? AND message_id = ? AND NOT success`, response.Success, response.Error, response.PublicKey, response.InstallationId, messageID) return err } diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 05eb42e3f..385f3cf14 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "testing" + "time" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/suite" @@ -211,6 +212,74 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() { s.Require().Equal(uint64(2), retrievedInfos[0].Version) } +func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() { + key, err := crypto.GenerateKey() + s.Require().NoError(err) + installationID := "installation-id" + messageID := []byte("message-id") + + sentNotification := &SentNotification{ + PublicKey: &key.PublicKey, + InstallationID: installationID, + MessageID: messageID, + SentAt: time.Now().Unix(), + } + + s.Require().NoError(s.persistence.NotifiedOn(sentNotification)) + + retrievedNotification, err := s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID) + s.Require().NoError(err) + s.Require().Equal(sentNotification, retrievedNotification) + + response := &protobuf.PushNotificationReport{ + Success: false, + Error: protobuf.PushNotificationReport_WRONG_TOKEN, + PublicKey: sentNotification.HashedPublicKey(), + InstallationId: installationID, + } + + s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response)) + + sentNotification.Error = protobuf.PushNotificationReport_WRONG_TOKEN + + retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID) + s.Require().NoError(err) + s.Require().Equal(sentNotification, retrievedNotification) + + // Update with a successful notification + response = &protobuf.PushNotificationReport{ + Success: true, + PublicKey: sentNotification.HashedPublicKey(), + InstallationId: installationID, + } + + s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response)) + + sentNotification.Success = true + sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE + + retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID) + s.Require().NoError(err) + s.Require().Equal(sentNotification, retrievedNotification) + + // Update with a unsuccessful notification, it should be ignored + response = &protobuf.PushNotificationReport{ + Success: false, + Error: protobuf.PushNotificationReport_WRONG_TOKEN, + PublicKey: sentNotification.HashedPublicKey(), + InstallationId: installationID, + } + + s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response)) + + sentNotification.Success = true + sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE + + retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID) + s.Require().NoError(err) + s.Require().Equal(sentNotification, retrievedNotification) +} + func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() { // Try with nil first retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration() From d775bb888a0e4a1217ca00f6e6a4ca9357791b34 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 14:46:15 +0200 Subject: [PATCH 40/46] Handle retries & use sendChatMessage for e2e tests --- protocol/common/message_processor.go | 2 + protocol/push_notification_client/client.go | 299 ++++++++++++------ .../migrations/migrations.go | 8 +- .../sql/1593601729_initial_schema.up.sql | 3 +- .../push_notification_client/persistence.go | 30 +- .../persistence_test.go | 17 +- protocol/push_notification_test.go | 21 +- 7 files changed, 278 insertions(+), 102 deletions(-) diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 738b5ca00..5c4720fc4 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -438,8 +438,10 @@ func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa MessageIDs: messageIDs, } + logger.Debug("subscriptions", zap.Int("count", len(p.subscriptions))) // Publish on channels, drop if buffer is full for _, c := range p.subscriptions { + logger.Debug("sending on subscription") select { case c <- sentMessage: default: diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 59f48680d..2f49ba45e 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -30,8 +30,14 @@ const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 -// maxRetries is the maximum number of attempts we do before giving up registering with a server -const maxRetries int64 = 12 +// maxRegistrationRetries is the maximum number of attempts we do before giving up registering with a server +const maxRegistrationRetries int64 = 12 + +// maxPushNotificationRetries is the maximum number of attempts before we give up sending a push notification +const maxPushNotificationRetries int64 = 4 + +// pushNotificationBackoffTime is the step of the exponential backoff +const pushNotificationBackoffTime int64 = 2 // RegistrationBackoffTime is the step of the exponential backoff const RegistrationBackoffTime int64 = 15 @@ -70,7 +76,8 @@ type PushNotificationInfo struct { type SentNotification struct { PublicKey *ecdsa.PublicKey InstallationID string - SentAt int64 + LastTriedAt int64 + RetryCount int64 MessageID []byte Success bool Error protobuf.PushNotificationReport_ErrorType @@ -125,6 +132,9 @@ type Client struct { // registrationLoopQuitChan is a channel to indicate to the registration loop that should be terminating registrationLoopQuitChan chan struct{} + + // resendingLoopQuitChan is a channel to indicate to the send loop that shoudl be terminating + resendingLoopQuitChan chan struct{} } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { @@ -139,6 +149,7 @@ func New(persistence *Persistence, config *Config, processor *common.MessageProc func (c *Client) subscribeForSentMessages() { go func() { + c.config.Logger.Info("subscribing for messages") subscription := c.messageProcessor.Subscribe() for { select { @@ -181,12 +192,26 @@ func (c *Client) stopRegistrationLoop() { } } +func (c *Client) stopResendingLoop() { + // stop old registration loop + if c.resendingLoopQuitChan != nil { + close(c.resendingLoopQuitChan) + c.resendingLoopQuitChan = nil + } +} + func (c *Client) startRegistrationLoop() { c.stopRegistrationLoop() c.registrationLoopQuitChan = make(chan struct{}) go c.registrationLoop() } +func (c *Client) startResendingLoop() { + c.stopResendingLoop() + c.resendingLoopQuitChan = make(chan struct{}) + go c.resendingLoop() +} + func (c *Client) Start() error { if c.messageProcessor == nil { return errors.New("can't start, missing message processor") @@ -198,19 +223,19 @@ func (c *Client) Start() error { } c.subscribeForSentMessages() c.startRegistrationLoop() + c.startResendingLoop() return nil } func (c *Client) Stop() error { close(c.quit) - if c.registrationLoopQuitChan != nil { - close(c.registrationLoopQuitChan) - } + c.stopRegistrationLoop() + c.stopResendingLoop() return nil } -func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey) error { +func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) error { // Check if we queried recently queriedAt, err := c.persistence.GetQueriedAt(publicKey) if err != nil { @@ -218,7 +243,7 @@ func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey) error { } // Naively query again if too much time has passed. // Here it might not be necessary - if time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { + if force || time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { c.config.Logger.Info("querying info") err := c.QueryPushNotificationInfo(publicKey) if err != nil { @@ -294,92 +319,21 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { c.config.Logger.Info("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) - err := c.queryNotificationInfo(publicKey) + infos, err := c.sendNotification(publicKey, installationIDs, trackedMessageIDs[0]) if err != nil { return err } - c.config.Logger.Info("queried info") - // Retrieve infos - info, err := c.GetPushNotificationInfo(publicKey, installationIDs) - if err != nil { - c.config.Logger.Error("could not get pn info", zap.Error(err)) - return err - } - - // Naively dispatch to the first server for now - // This wait for an acknowledgement and try a different server after a timeout - // Also we sent a single notification for multiple message ids, need to check with UI what's the desired behavior - - // Sort by server so we tend to hit the same one - sort.Slice(info, func(i, j int) bool { - return info[i].ServerPublicKey.X.Cmp(info[j].ServerPublicKey.X) <= 0 - }) - - c.config.Logger.Info("retrieved info") - - installationIDsMap := make(map[string]bool) - // One info per installation id, grouped by server - actionableInfos := make(map[string][]*PushNotificationInfo) - for _, i := range info { - - c.config.Logger.Info("queried info", zap.String("id", i.InstallationID)) - if !installationIDsMap[i.InstallationID] { - serverKey := hex.EncodeToString(crypto.CompressPubkey(i.ServerPublicKey)) - actionableInfos[serverKey] = append(actionableInfos[serverKey], i) - installationIDsMap[i.InstallationID] = true - } - - } - - c.config.Logger.Info("actionable info", zap.Int("count", len(actionableInfos))) - - for _, infos := range actionableInfos { - var pushNotifications []*protobuf.PushNotification - for _, i := range infos { - // TODO: Add ChatID, message, public_key - pushNotifications = append(pushNotifications, &protobuf.PushNotification{ - AccessToken: i.AccessToken, - PublicKey: common.HashPublicKey(publicKey), - InstallationId: i.InstallationID, - }) - - } - request := &protobuf.PushNotificationRequest{ - MessageId: trackedMessageIDs[0], - Requests: pushNotifications, - } - serverPublicKey := infos[0].ServerPublicKey - - payload, err := proto.Marshal(request) - if err != nil { - return err - } - - rawMessage := &common.RawMessage{ - Payload: payload, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, - } - - // TODO: We should use the messageID for the response - _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, rawMessage) - - if err != nil { - return err - } - - // Mark message as sent, this is at-most-once semantic - // for all messageIDs - for _, i := range infos { - for _, messageID := range trackedMessageIDs { - - c.config.Logger.Info("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) - if err := c.notifiedOn(publicKey, i.InstallationID, messageID); err != nil { - return err - } + // Mark message as sent, this is at-most-once semantic + // for all messageIDs + for _, i := range infos { + for _, messageID := range trackedMessageIDs { + c.config.Logger.Info("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) + if err := c.notifiedOn(publicKey, i.InstallationID, messageID); err != nil { + return err } - } + } } return nil @@ -399,9 +353,9 @@ func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID strin } func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { - return c.persistence.NotifiedOn(&SentNotification{ + return c.persistence.UpsertSentNotification(&SentNotification{ PublicKey: publicKey, - SentAt: time.Now().Unix(), + LastTriedAt: time.Now().Unix(), InstallationID: installationID, MessageID: messageID, }) @@ -532,11 +486,26 @@ func nextServerRetry(server *PushNotificationServer) int64 { return server.LastRetriedAt + RegistrationBackoffTime*server.RetryCount*int64(math.Exp2(float64(server.RetryCount))) } +func nextPushNotificationRetry(pn *SentNotification) int64 { + return pn.LastTriedAt + pushNotificationBackoffTime*pn.RetryCount*int64(math.Exp2(float64(pn.RetryCount))) +} + // We calculate if it's too early to retry, by exponentially backing off func shouldRetryRegisteringWithServer(server *PushNotificationServer) bool { + if server.RetryCount > maxRegistrationRetries { + return false + } return time.Now().Unix() > nextServerRetry(server) } +// We calculate if it's too early to retry, by exponentially backing off +func shouldRetryPushNotification(pn *SentNotification) bool { + if pn.RetryCount > maxPushNotificationRetries { + return false + } + return time.Now().Unix() > nextPushNotificationRetry(pn) +} + func (c *Client) resetServers() error { servers, err := c.persistence.GetServers() if err != nil { @@ -603,6 +572,153 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis return nil } +func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []string, messageID []byte) ([]*PushNotificationInfo, error) { + err := c.queryNotificationInfo(publicKey, false) + if err != nil { + return nil, err + } + c.config.Logger.Info("queried info") + // Retrieve infos + info, err := c.GetPushNotificationInfo(publicKey, installationIDs) + if err != nil { + c.config.Logger.Error("could not get pn info", zap.Error(err)) + return nil, err + } + + // Naively dispatch to the first server for now + // This wait for an acknowledgement and try a different server after a timeout + // Also we sent a single notification for multiple message ids, need to check with UI what's the desired behavior + + // Sort by server so we tend to hit the same one + sort.Slice(info, func(i, j int) bool { + return info[i].ServerPublicKey.X.Cmp(info[j].ServerPublicKey.X) <= 0 + }) + + c.config.Logger.Info("retrieved info") + + installationIDsMap := make(map[string]bool) + // One info per installation id, grouped by server + actionableInfos := make(map[string][]*PushNotificationInfo) + for _, i := range info { + + c.config.Logger.Info("queried info", zap.String("id", i.InstallationID)) + if !installationIDsMap[i.InstallationID] { + serverKey := hex.EncodeToString(crypto.CompressPubkey(i.ServerPublicKey)) + actionableInfos[serverKey] = append(actionableInfos[serverKey], i) + installationIDsMap[i.InstallationID] = true + } + + } + + c.config.Logger.Info("actionable info", zap.Int("count", len(actionableInfos))) + + var actionedInfo []*PushNotificationInfo + for _, infos := range actionableInfos { + var pushNotifications []*protobuf.PushNotification + for _, i := range infos { + // TODO: Add ChatID, message, public_key + pushNotifications = append(pushNotifications, &protobuf.PushNotification{ + AccessToken: i.AccessToken, + PublicKey: common.HashPublicKey(publicKey), + InstallationId: i.InstallationID, + }) + + } + request := &protobuf.PushNotificationRequest{ + MessageId: messageID, + Requests: pushNotifications, + } + serverPublicKey := infos[0].ServerPublicKey + + payload, err := proto.Marshal(request) + if err != nil { + return nil, err + } + + rawMessage := &common.RawMessage{ + Payload: payload, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, + } + + // TODO: We should use the messageID for the response + _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, rawMessage) + + if err != nil { + return nil, err + } + actionedInfo = append(actionedInfo, infos...) + } + return actionedInfo, nil +} + +func (c *Client) resendNotification(pn *SentNotification) error { + c.config.Logger.Info("resending notification", zap.Any("notification", pn)) + pn.RetryCount += 1 + pn.LastTriedAt = time.Now().Unix() + err := c.persistence.UpsertSentNotification(pn) + if err != nil { + return err + } + + // Re-fetch push notification info + err = c.queryNotificationInfo(pn.PublicKey, true) + if err != nil { + return err + } + + if err != nil { + c.config.Logger.Error("could not get pn info", zap.Error(err)) + return err + } + + _, err = c.sendNotification(pn.PublicKey, []string{pn.InstallationID}, pn.MessageID) + return err +} + +func (c *Client) resendingLoop() error { + for { + c.config.Logger.Info("running resending loop") + var lowestNextRetry int64 + + retriableNotifications, err := c.persistence.GetRetriablePushNotifications() + if err != nil { + c.config.Logger.Error("failed retrieving notifications, quitting resending loop", zap.Error(err)) + return err + } + + if len(retriableNotifications) == 0 { + c.config.Logger.Debug("no retriable notifications, quitting") + return nil + } + + for _, pn := range retriableNotifications { + nextRetry := nextPushNotificationRetry(pn) + c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nextRetry)) + if shouldRetryPushNotification(pn) { + c.config.Logger.Info("retrying pn", zap.Any("pn", pn)) + err := c.resendNotification(pn) + if err != nil { + return err + } + } + if lowestNextRetry == 0 || nextRetry < lowestNextRetry { + lowestNextRetry = nextRetry + } + } + + nextRetry := lowestNextRetry - time.Now().Unix() + waitFor := time.Duration(nextRetry) + select { + + case <-time.After(waitFor * time.Second): + case <-c.resendingLoopQuitChan: + return nil + + } + + } +} + func (c *Client) registrationLoop() error { for { c.config.Logger.Info("running registration loop") @@ -882,6 +998,9 @@ func (c *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, resp return err } } + // Restart resending loop + c.stopResendingLoop() + c.startResendingLoop() return nil } diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index 431630032..2743a2282 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.709kB) +// 1593601729_initial_schema.up.sql (1.753kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x6e\xe2\x30\x10\xbd\xe7\x2b\xe6\x58\x24\x0e\x7b\xef\x29\x50\xb3\x8a\x64\x39\xbb\x60\x24\x6e\x96\xd7\x99\x36\x16\x59\xa7\x6b\x3b\xd5\xf2\xf7\x2b\x27\x90\x42\x9d\x75\xa4\x96\x0b\x12\x33\xcf\xa3\x79\x6f\x5e\xde\x7a\x4b\x72\x4e\x80\xe7\x2b\x4a\xa0\xd8\x00\x2b\x39\x90\x43\xb1\xe3\x3b\x78\xed\x5c\x2d\x4c\xeb\xf5\xb3\x56\xd2\xeb\xd6\x08\xd5\x68\x34\x5e\x38\xb4\x6f\x68\x1d\x3c\x64\x00\xaf\xdd\xaf\x46\x2b\x71\xc4\x13\xac\x68\xb9\xea\xdf\xb3\x3d\xa5\xcb\x0c\xc0\xe2\x8b\x76\x1e\x2d\x56\xb0\x2a\x4b\x4a\x72\x06\x4f\x64\x93\xef\x29\x87\x4d\x4e\x77\xe4\x16\x23\xa4\x87\x82\xf1\x71\xc2\x88\xfd\x16\x70\x8d\x74\x5e\x58\xf4\x56\xcf\x21\x03\xe8\x24\x54\xdb\x99\x14\x4a\x2a\x85\xce\x09\xdf\x1e\xd1\x00\x27\x07\x1e\x8a\x7b\x56\xfc\xdc\x93\x87\x77\x4e\x0b\x28\x19\xac\x4b\xb6\xa1\xc5\x9a\xc3\x96\xfc\xa0\xf9\x9a\x64\x8b\xc7\x2c\xfb\x8c\x6e\x7f\x3a\xb4\x1a\xe7\x75\x1b\x70\x11\xcd\x4b\xeb\x24\x74\x15\x3f\x8a\x76\x5f\x5e\xb0\xf7\x25\xa1\xcd\x73\x3b\xcb\x60\x70\x88\x48\x41\xb4\x71\x5e\x36\xcd\x30\x5b\x57\xfd\x0d\x6e\x00\xd1\x85\x3e\x78\x2b\x58\xe1\x6d\x5a\xa5\xe0\x4e\xdd\x9a\xa8\x1e\x6b\xf4\x71\x8d\x65\xbc\xfa\x7d\xe5\xf3\x56\xaa\x23\x56\xe2\x37\x3a\x27\x5f\xce\x66\x38\xff\x99\xbc\xab\xaa\xa5\x9f\xd4\xe7\x32\x69\x82\xff\x99\xe7\xfb\xd8\x5b\x0e\xc5\x77\x56\x6e\x49\x06\xf0\x59\x12\x2e\xfc\x5c\x37\xe6\x69\xa4\xac\x50\x4b\x57\x63\xf5\x35\xb7\xf4\x2b\x4d\x48\xe1\xba\xde\x46\x63\x00\x45\x69\x30\x26\x11\x5a\xdb\xda\x44\x62\x44\xa2\x2e\x21\x61\xa4\xc5\x17\xe4\x1d\x32\xd1\x5e\x29\x7b\xc9\xc9\xa1\x16\xcb\x03\xa0\x5a\xe3\xa5\x0a\x4e\x71\x7d\x7b\xa8\xba\x93\xf1\x35\x7a\xad\x82\x66\xff\xa7\x36\x92\xbb\xc6\xcf\xfa\xbe\x60\x4f\xe4\x00\xba\xfa\x2b\x92\x61\x71\x7d\xd7\x92\xa5\x83\x25\xf5\x69\x2e\x1e\xb3\x7f\x01\x00\x00\xff\xff\xed\x10\xc3\xcd\xad\x06\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x6e\xe3\x20\x10\xbd\xfb\x2b\xe6\xd8\x48\x39\xec\xbd\x27\x27\x25\x2b\x4b\x08\xef\x26\x44\xca\x0d\xb1\x78\x5a\xa3\x78\x71\x17\x70\xb5\xf9\xfb\x15\x76\xe2\x26\xc5\x8b\xab\xb6\x17\x4b\x1e\x1e\xa3\x79\x6f\x1e\x6f\xbd\x25\x39\x27\xc0\xf3\x15\x25\x50\x6c\x80\x95\x1c\xc8\xa1\xd8\xf1\x1d\x3c\x77\xae\x16\xa6\xf5\xfa\x51\x2b\xe9\x75\x6b\x84\x6a\x34\x1a\x2f\x1c\xda\x17\xb4\x0e\xee\x32\x80\xe7\xee\x57\xa3\x95\x38\xe2\x09\x56\xb4\x5c\xf5\xf7\xd9\x9e\xd2\x65\x06\x60\xf1\x49\x3b\x8f\x16\x2b\x58\x95\x25\x25\x39\x83\x07\xb2\xc9\xf7\x94\xc3\x26\xa7\x3b\x72\x8b\x11\xd2\x43\xc1\xf8\xd8\x61\xc4\x7e\x0b\xb8\x46\x3a\x2f\x2c\x7a\xab\xe7\x90\x01\x74\x12\xaa\xed\x4c\x0a\x25\x95\x42\xe7\x84\x6f\x8f\x68\x80\x93\x03\x0f\xc5\x3d\x2b\x7e\xee\xc9\xdd\x2b\xa7\x05\x94\x0c\xd6\x25\xdb\xd0\x62\xcd\x61\x4b\x7e\xd0\x7c\x4d\xb2\xc5\x7d\x96\x7d\x44\xb7\x3f\x1d\x5a\x8d\xf3\xba\x0d\xb8\x88\xe6\xe5\xe8\x24\x74\x15\x5f\x8a\x66\x5f\x5e\xb0\x5f\x4b\x42\x9b\xc7\x76\x96\xc1\xe0\x10\x91\x82\x68\xe3\xbc\x6c\x9a\xa1\xb7\xae\xfa\x1d\xdc\x00\xa2\x0d\xbd\xf1\x56\xb0\xc2\xcb\xb4\x4a\xc1\x9d\xba\x35\x51\x3d\xd6\xe8\xed\x18\xcb\x78\xf4\xaf\x95\xcf\x5b\xa9\x8e\x58\x89\xdf\xe8\x9c\x7c\x3a\x9b\xe1\xfc\x33\xb9\x57\x55\x4b\x3f\xa9\xcf\xa5\xd3\x04\xff\x33\xcf\xd7\xb6\xb7\x1c\x8a\xef\xac\xdc\x92\x0c\xe0\xa3\x24\x5c\xf8\x5c\x1f\xcc\xd3\x48\x59\xa1\x96\xae\xc6\xea\x73\x6e\xe9\xf3\x61\x32\x1d\xde\x9f\x09\xae\xeb\x2d\x37\x86\x55\x84\x1a\x53\x0b\xad\x6d\x6d\xa2\x53\xb4\x80\x25\x24\x4c\xb7\xf8\xc4\x2a\x86\xfc\xb4\x57\x5b\xb8\x64\xea\x50\x8b\xa5\x04\x50\xad\xf1\x52\x05\x57\xb9\xfe\x78\xa8\xba\x93\xf1\x35\x7a\xad\x82\xbe\xff\xa7\x36\x92\xbb\xc6\xcf\xbe\x91\x82\x3d\x90\x03\xe8\xea\xaf\x48\x06\xcb\xb5\x07\x4a\x96\x0e\xa1\xd4\x33\x5e\xdc\x67\xff\x02\x00\x00\xff\xff\x39\x9f\x6b\x23\xd9\x06\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1709, mode: os.FileMode(0644), modTime: time.Unix(1595237467, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x35, 0x40, 0x6a, 0x4a, 0x45, 0x37, 0x37, 0x99, 0x97, 0x5, 0xb3, 0x43, 0x6, 0x43, 0xcc, 0x10, 0x32, 0xbc, 0x16, 0xcc, 0xe0, 0xfb, 0x3, 0xa8, 0xce, 0x6a, 0x6b, 0x39, 0xd4, 0xe0, 0xbe, 0xa4}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1753, mode: os.FileMode(0644), modTime: time.Unix(1595240420, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x28, 0xbf, 0x64, 0xe0, 0x65, 0x53, 0xd3, 0x80, 0xf4, 0x46, 0xce, 0xd6, 0x23, 0x4e, 0xc5, 0x8f, 0x80, 0x4e, 0x91, 0xa7, 0x2e, 0x9, 0x3b, 0xf4, 0x5f, 0xa1, 0xff, 0xfc, 0x6e, 0x4, 0xa2, 0xe7}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index d4e4941e6..1af27e9de 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -37,7 +37,8 @@ CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( public_key BLOB NOT NULL, hashed_public_key BLOB NOT NULL, installation_id TEXT NOT NULL, - sent_at INT NOT NULL, + last_tried_at INT NOT NULL, + retry_count INT NOT NULL DEFAULT 0, success BOOLEAN NOT NULL DEFAULT FALSE, error INT NOT NULL DEFAULT 0, UNIQUE(message_id, public_key, installation_id) diff --git a/protocol/push_notification_client/persistence.go b/protocol/push_notification_client/persistence.go index 42fd128c9..bc657be94 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/push_notification_client/persistence.go @@ -271,8 +271,8 @@ func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecds return count == 0, nil } -func (p *Persistence) NotifiedOn(n *SentNotification) error { - _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, sent_at, hashed_public_key) VALUES (?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.SentAt, n.HashedPublicKey()) +func (p *Persistence) UpsertSentNotification(n *SentNotification) error { + _, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, last_tried_at, retry_count, success, error, hashed_public_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.LastTriedAt, n.RetryCount, n.Success, n.Error, n.HashedPublicKey()) return err } @@ -282,7 +282,7 @@ func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID InstallationID: installationID, MessageID: messageID, } - err := p.db.QueryRow(`SELECT sent_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.SentAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes) + err := p.db.QueryRow(`SELECT retry_count, last_tried_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.RetryCount, &sentNotification.LastTriedAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes) if err != nil { return nil, err } @@ -302,6 +302,30 @@ func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *pro return err } +func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) { + var notifications []*SentNotification + rows, err := p.db.Query(`SELECT retry_count, last_tried_at, error, success, public_key, installation_id, message_id FROM push_notification_client_sent_notifications WHERE NOT success AND error = ?`, protobuf.PushNotificationReport_WRONG_TOKEN) + if err != nil { + return nil, err + } + + for rows.Next() { + var publicKeyBytes []byte + notification := &SentNotification{} + err = rows.Scan(¬ification.RetryCount, ¬ification.LastTriedAt, ¬ification.Error, ¬ification.Success, &publicKeyBytes, ¬ification.InstallationID, ¬ification.MessageID) + if err != nil { + return nil, err + } + publicKey, err := crypto.DecompressPubkey(publicKeyBytes) + if err != nil { + return nil, err + } + notification.PublicKey = publicKey + notifications = append(notifications, notification) + } + return notifications, err +} + func (p *Persistence) UpsertServer(server *PushNotificationServer) error { _, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token, last_retried_at, retry_count) VALUES (?,?,?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken, server.LastRetriedAt, server.RetryCount) return err diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/push_notification_client/persistence_test.go index 385f3cf14..8f522c767 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/push_notification_client/persistence_test.go @@ -222,15 +222,19 @@ func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() { PublicKey: &key.PublicKey, InstallationID: installationID, MessageID: messageID, - SentAt: time.Now().Unix(), + LastTriedAt: time.Now().Unix(), } - s.Require().NoError(s.persistence.NotifiedOn(sentNotification)) + s.Require().NoError(s.persistence.UpsertSentNotification(sentNotification)) retrievedNotification, err := s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID) s.Require().NoError(err) s.Require().Equal(sentNotification, retrievedNotification) + retriableNotifications, err := s.persistence.GetRetriablePushNotifications() + s.Require().NoError(err) + s.Require().Len(retriableNotifications, 0) + response := &protobuf.PushNotificationReport{ Success: false, Error: protobuf.PushNotificationReport_WRONG_TOKEN, @@ -239,6 +243,10 @@ func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() { } s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response)) + // This notification should be retriable + retriableNotifications, err = s.persistence.GetRetriablePushNotifications() + s.Require().NoError(err) + s.Require().Len(retriableNotifications, 1) sentNotification.Error = protobuf.PushNotificationReport_WRONG_TOKEN @@ -262,6 +270,11 @@ func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() { s.Require().NoError(err) s.Require().Equal(sentNotification, retrievedNotification) + // This notification should not be retriable + retriableNotifications, err = s.persistence.GetRetriablePushNotifications() + s.Require().NoError(err) + s.Require().Len(retriableNotifications, 0) + // Update with a unsuccessful notification, it should be ignored response = &protobuf.PushNotificationReport{ Success: false, diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index ddac6e8fe..0640470a8 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -3,6 +3,7 @@ package protocol import ( "context" "crypto/ecdsa" + "encoding/hex" "errors" "io/ioutil" "os" @@ -120,6 +121,9 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) alice := s.newMessenger(s.shh) + // start alice and enable sending push notifications + s.Require().NoError(alice.Start()) + s.Require().NoError(alice.EnableSendingPushNotifications()) bobInstallationIDs := []string{bob1.installationID, bob2.installationID} // Register bob1 @@ -183,7 +187,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { bob2Servers, err := bob2.GetPushNotificationServers() s.Require().NoError(err) - err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) + // Create one to one chat & send message + pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) + chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) + s.Require().NoError(alice.SaveChat(&chat)) + inputMessage := buildTestMessage(chat) + _, err = alice.SendChatMessage(context.Background(), inputMessage) s.Require().NoError(err) var info []*push_notification_client.PushNotificationInfo @@ -247,6 +256,9 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) alice := s.newMessenger(s.shh) + // start alice and enable push notifications + s.Require().NoError(alice.Start()) + s.Require().NoError(alice.EnableSendingPushNotifications()) bobInstallationIDs := []string{bob.installationID, bob2.installationID} // Register bob @@ -294,7 +306,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO bobServers, err := bob.GetPushNotificationServers() s.Require().NoError(err) - err = alice.pushNotificationClient.QueryPushNotificationInfo(&bob2.identity.PublicKey) + // Create one to one chat & send message + pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) + chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) + s.Require().NoError(alice.SaveChat(&chat)) + inputMessage := buildTestMessage(chat) + _, err = alice.SendChatMessage(context.Background(), inputMessage) s.Require().NoError(err) var info []*push_notification_client.PushNotificationInfo From 149fc5e3ebc020e08cb7ac7a9185bc9cb36ca9f4 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 15:58:54 +0200 Subject: [PATCH 41/46] Test token invalidation --- protocol/messenger.go | 29 +++- protocol/push_notification_client/client.go | 33 +++-- protocol/push_notification_test.go | 150 +++++++++++++++++++- 3 files changed, 199 insertions(+), 13 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index 1127b1e8c..db1297f76 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1121,6 +1121,14 @@ func (m *Messenger) isNewContact(contact *Contact) bool { return contact.IsAdded() && (!ok || !previousContact.IsAdded()) } +func (m *Messenger) removedContact(contact *Contact) bool { + previousContact, ok := m.allContacts[contact.ID] + if !ok { + return false + } + return previousContact.IsAdded() && !contact.IsAdded() +} + func (m *Messenger) saveContact(contact *Contact) error { name, identicon, err := generateAliasAndIdenticon(contact.ID) if err != nil { @@ -1137,6 +1145,9 @@ func (m *Messenger) saveContact(contact *Contact) error { } } + // We check if it should re-register + shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact)) + err = m.persistence.SaveContact(contact, nil) if err != nil { return err @@ -1144,6 +1155,16 @@ func (m *Messenger) saveContact(contact *Contact) error { m.allContacts[contact.ID] = contact + // Reregister only when data has changed + if shouldReregisterForPushNotifications { + m.logger.Info("contact state changed, re-registering for push notification") + contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() + err := m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs) + if err != nil { + return err + } + } + return nil } @@ -3087,8 +3108,6 @@ func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string var contactIDs []*ecdsa.PublicKey var mutedChatIDs []string - m.mutex.Lock() - defer m.mutex.Unlock() for _, contact := range m.allContacts { if contact.IsAdded() { pk, err := contact.PublicKey() @@ -3115,6 +3134,8 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke if m.pushNotificationClient == nil { return errors.New("push notification client not enabled") } + m.mutex.Lock() + defer m.mutex.Unlock() contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) @@ -3131,6 +3152,8 @@ func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { if m.pushNotificationClient == nil { return errors.New("no push notification client") } + m.mutex.Lock() + defer m.mutex.Unlock() contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) @@ -3140,6 +3163,8 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { if m.pushNotificationClient == nil { return errors.New("no push notification client") } + m.mutex.Lock() + defer m.mutex.Unlock() contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs() return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 2f49ba45e..7ed8957e5 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -424,6 +424,7 @@ func (c *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([ // and return a new one in that case func (c *Client) getToken(contactIDs []*ecdsa.PublicKey) string { if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs) { + c.config.Logger.Info("refreshing access token") return uuid.New().String() } return c.lastPushNotificationRegistration.AccessToken @@ -492,10 +493,7 @@ func nextPushNotificationRetry(pn *SentNotification) int64 { // We calculate if it's too early to retry, by exponentially backing off func shouldRetryRegisteringWithServer(server *PushNotificationServer) bool { - if server.RetryCount > maxRegistrationRetries { - return false - } - return time.Now().Unix() > nextServerRetry(server) + return time.Now().Unix() >= nextServerRetry(server) } // We calculate if it's too early to retry, by exponentially backing off @@ -516,14 +514,13 @@ func (c *Client) resetServers() error { // Reset server registration data server.Registered = false server.RegisteredAt = 0 - server.RetryCount += 1 + server.RetryCount = 0 server.LastRetriedAt = time.Now().Unix() server.AccessToken = "" if err := c.persistence.UpsertServer(server); err != nil { return err } - } return nil @@ -692,8 +689,8 @@ func (c *Client) resendingLoop() error { } for _, pn := range retriableNotifications { - nextRetry := nextPushNotificationRetry(pn) - c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nextRetry)) + nR := nextPushNotificationRetry(pn) + c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nR)) if shouldRetryPushNotification(pn) { c.config.Logger.Info("retrying pn", zap.Any("pn", pn)) err := c.resendNotification(pn) @@ -701,6 +698,7 @@ func (c *Client) resendingLoop() error { return err } } + nextRetry := nextPushNotificationRetry(pn) if lowestNextRetry == 0 || nextRetry < lowestNextRetry { lowestNextRetry = nextRetry } @@ -734,7 +732,7 @@ func (c *Client) registrationLoop() error { var nonRegisteredServers []*PushNotificationServer for _, server := range servers { - if !server.Registered { + if !server.Registered && server.RetryCount < maxRegistrationRetries { nonRegisteredServers = append(nonRegisteredServers, server) } } @@ -766,6 +764,7 @@ func (c *Client) registrationLoop() error { nextRetry := lowestNextRetry - time.Now().Unix() waitFor := time.Duration(nextRetry) + c.config.Logger.Info("Waiting for", zap.Any("wait for", waitFor)) select { case <-time.After(waitFor * time.Second): @@ -830,10 +829,26 @@ func (c *Client) GetServers() ([]*PushNotificationServer, error) { return c.persistence.GetServers() } +func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.Logger.Info("re-registering") + if len(c.deviceToken) == 0 { + c.config.Logger.Info("no device token, not registering") + return nil + } + + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) +} + func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { // stop registration loop c.stopRegistrationLoop() + // Reset servers + err := c.resetServers() + if err != nil { + return err + } + c.deviceToken = deviceToken registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 0640470a8..58b2c429d 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -253,13 +253,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO bobDeviceToken := "token-1" bob := s.m - bob2 := s.newMessengerWithKey(s.shh, s.m.identity) server := s.newPushNotificationServer(s.shh) alice := s.newMessenger(s.shh) // start alice and enable push notifications s.Require().NoError(alice.Start()) s.Require().NoError(alice.EnableSendingPushNotifications()) - bobInstallationIDs := []string{bob.installationID, bob2.installationID} + bobInstallationIDs := []string{bob.installationID} // Register bob err := bob.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) @@ -348,3 +347,150 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 1) } + +func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { + + bobDeviceToken := "token-1" + + bob := s.m + server := s.newPushNotificationServer(s.shh) + alice := s.newMessenger(s.shh) + // another contact to invalidate the token + frank := s.newMessenger(s.shh) + // start alice and enable push notifications + s.Require().NoError(alice.Start()) + s.Require().NoError(alice.EnableSendingPushNotifications()) + bobInstallationIDs := []string{bob.installationID} + + // Register bob + err := bob.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + // Add alice has a contact + aliceContact := &Contact{ + ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)), + Name: "Some Contact", + SystemTags: []string{contactAdded}, + } + + err = bob.SaveContact(aliceContact) + s.Require().NoError(err) + + // Add frank has a contact + frankContact := &Contact{ + ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)), + Name: "Some Contact", + SystemTags: []string{contactAdded}, + } + + err = bob.SaveContact(frankContact) + s.Require().NoError(err) + + // Enable from contacts only + err = bob.EnablePushNotificationsFromContactsOnly() + s.Require().NoError(err) + + err = bob.RegisterForPushNotifications(context.Background(), bobDeviceToken) + s.Require().NoError(err) + + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob.RetrieveAll() + if err != nil { + return err + } + registered, err := bob.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bobServers, err := bob.GetPushNotificationServers() + s.Require().NoError(err) + + // Create one to one chat & send message + pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) + chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) + s.Require().NoError(alice.SaveChat(&chat)) + inputMessage := buildTestMessage(chat) + _, err = alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + + // The message has been sent, but not received, now we remove a contact so that the token is invalidated + frankContact = &Contact{ + ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)), + Name: "Some Contact", + SystemTags: []string{}, + } + err = bob.SaveContact(frankContact) + s.Require().NoError(err) + + // Re-registration should be triggered, pull from server and bob to check we are correctly registered + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob.RetrieveAll() + if err != nil { + return err + } + registered, err := bob.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + + newBobServers, err := bob.GetPushNotificationServers() + s.Require().NoError(err) + // Make sure access token is not the same + s.Require().NotEqual(newBobServers[0].AccessToken, bobServers[0].AccessToken) + + var info []*push_notification_client.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for bob + if len(info) != 1 { + return errors.New("info not fetched") + } + return nil + + }) + s.Require().NoError(err) + + s.Require().NotNil(info) + s.Require().Equal(bob.installationID, info[0].InstallationID) + s.Require().Equal(newBobServers[0].AccessToken, info[0].AccessToken) + s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey) + + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 1) +} From 59d1ee4bb9f1a81db073a7f687eb204d4ae378b6 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 16:14:42 +0200 Subject: [PATCH 42/46] full e2e tests --- protocol/push_notification_client/client.go | 4 + protocol/push_notification_test.go | 87 ++++++++++++++++++++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 7ed8957e5..638fe9731 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -825,6 +825,10 @@ func (c *Client) Registered() (bool, error) { return true, nil } +func (c *Client) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) { + return c.persistence.GetSentNotification(hashedPublicKey, installationID, messageID) +} + func (c *Client) GetServers() ([]*PushNotificationServer, error) { return c.persistence.GetServers() } diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 58b2c429d..fc39a5df5 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -16,6 +16,7 @@ import ( 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/push_notification_client" "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/tt" @@ -192,7 +193,10 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) s.Require().NoError(alice.SaveChat(&chat)) inputMessage := buildTestMessage(chat) - _, err = alice.SendChatMessage(context.Background(), inputMessage) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) s.Require().NoError(err) var info []*push_notification_client.PushNotificationInfo @@ -246,6 +250,29 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NoError(err) s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 2) + + var sentNotification *push_notification_client.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { @@ -310,7 +337,10 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) s.Require().NoError(alice.SaveChat(&chat)) inputMessage := buildTestMessage(chat) - _, err = alice.SendChatMessage(context.Background(), inputMessage) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) s.Require().NoError(err) var info []*push_notification_client.PushNotificationInfo @@ -346,6 +376,30 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO s.Require().NoError(err) s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 1) + + var sentNotification *push_notification_client.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) + } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { @@ -422,7 +476,11 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) s.Require().NoError(alice.SaveChat(&chat)) inputMessage := buildTestMessage(chat) - _, err = alice.SendChatMessage(context.Background(), inputMessage) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) s.Require().NoError(err) // The message has been sent, but not received, now we remove a contact so that the token is invalidated @@ -493,4 +551,27 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { s.Require().NoError(err) s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 1) + + var sentNotification *push_notification_client.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) } From 2be8dff54a79edd9add560a8063b2f9565fdcf24 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 20 Jul 2020 16:52:55 +0200 Subject: [PATCH 43/46] Make sure pn is resent and succesful in tests --- protocol/push_notification_client/client.go | 12 ++++- .../migrations/migrations.go | 8 ++-- .../sql/1593601729_initial_schema.up.sql | 2 +- protocol/push_notification_test.go | 48 +++++++++++++++++-- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index 638fe9731..bdc99c8b7 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -236,11 +236,13 @@ func (c *Client) Stop() error { } func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) error { + c.config.Logger.Info("Getting queried at") // Check if we queried recently queriedAt, err := c.persistence.GetQueriedAt(publicKey) if err != nil { return err } + c.config.Logger.Info("checking if querying necessary") // Naively query again if too much time has passed. // Here it might not be necessary if force || time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { @@ -353,6 +355,7 @@ func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID strin } func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { + c.config.Logger.Info("notified on") return c.persistence.UpsertSentNotification(&SentNotification{ PublicKey: publicKey, LastTriedAt: time.Now().Unix(), @@ -501,7 +504,7 @@ func shouldRetryPushNotification(pn *SentNotification) bool { if pn.RetryCount > maxPushNotificationRetries { return false } - return time.Now().Unix() > nextPushNotificationRetry(pn) + return time.Now().Unix() >= nextPushNotificationRetry(pn) } func (c *Client) resetServers() error { @@ -652,14 +655,18 @@ func (c *Client) resendNotification(pn *SentNotification) error { c.config.Logger.Info("resending notification", zap.Any("notification", pn)) pn.RetryCount += 1 pn.LastTriedAt = time.Now().Unix() + c.config.Logger.Info("PN", zap.Any("pn", pn)) err := c.persistence.UpsertSentNotification(pn) if err != nil { + c.config.Logger.Error("failed to upsert notification", zap.Error(err)) return err } + c.config.Logger.Info("GOING INTO querying") // Re-fetch push notification info err = c.queryNotificationInfo(pn.PublicKey, true) if err != nil { + c.config.Logger.Error("failed to query notification info", zap.Error(err)) return err } @@ -699,6 +706,7 @@ func (c *Client) resendingLoop() error { } } nextRetry := nextPushNotificationRetry(pn) + c.config.Logger.Info("Next next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nextRetry), zap.Any("pn", pn)) if lowestNextRetry == 0 || nextRetry < lowestNextRetry { lowestNextRetry = nextRetry } @@ -1011,7 +1019,9 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ // HandlePushNotificationResponse should set the request as processed func (c *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { messageID := response.MessageId + c.config.Logger.Info("received response for", zap.Binary("message-id", messageID)) for _, report := range response.Reports { + c.config.Logger.Info("received response", zap.Any("report", report)) err := c.persistence.UpdateNotificationResponse(messageID, report) if err != nil { return err diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/push_notification_client/migrations/migrations.go index 2743a2282..61cd5e54d 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/push_notification_client/migrations/migrations.go @@ -1,7 +1,7 @@ // Code generated by go-bindata. DO NOT EDIT. // sources: // 1593601729_initial_schema.down.sql (144B) -// 1593601729_initial_schema.up.sql (1.753kB) +// 1593601729_initial_schema.up.sql (1.773kB) // doc.go (382B) package migrations @@ -91,7 +91,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return a, nil } -var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x6e\xe3\x20\x10\xbd\xfb\x2b\xe6\xd8\x48\x39\xec\xbd\x27\x27\x25\x2b\x4b\x08\xef\x26\x44\xca\x0d\xb1\x78\x5a\xa3\x78\x71\x17\x70\xb5\xf9\xfb\x15\x76\xe2\x26\xc5\x8b\xab\xb6\x17\x4b\x1e\x1e\xa3\x79\x6f\x1e\x6f\xbd\x25\x39\x27\xc0\xf3\x15\x25\x50\x6c\x80\x95\x1c\xc8\xa1\xd8\xf1\x1d\x3c\x77\xae\x16\xa6\xf5\xfa\x51\x2b\xe9\x75\x6b\x84\x6a\x34\x1a\x2f\x1c\xda\x17\xb4\x0e\xee\x32\x80\xe7\xee\x57\xa3\x95\x38\xe2\x09\x56\xb4\x5c\xf5\xf7\xd9\x9e\xd2\x65\x06\x60\xf1\x49\x3b\x8f\x16\x2b\x58\x95\x25\x25\x39\x83\x07\xb2\xc9\xf7\x94\xc3\x26\xa7\x3b\x72\x8b\x11\xd2\x43\xc1\xf8\xd8\x61\xc4\x7e\x0b\xb8\x46\x3a\x2f\x2c\x7a\xab\xe7\x90\x01\x74\x12\xaa\xed\x4c\x0a\x25\x95\x42\xe7\x84\x6f\x8f\x68\x80\x93\x03\x0f\xc5\x3d\x2b\x7e\xee\xc9\xdd\x2b\xa7\x05\x94\x0c\xd6\x25\xdb\xd0\x62\xcd\x61\x4b\x7e\xd0\x7c\x4d\xb2\xc5\x7d\x96\x7d\x44\xb7\x3f\x1d\x5a\x8d\xf3\xba\x0d\xb8\x88\xe6\xe5\xe8\x24\x74\x15\x5f\x8a\x66\x5f\x5e\xb0\x5f\x4b\x42\x9b\xc7\x76\x96\xc1\xe0\x10\x91\x82\x68\xe3\xbc\x6c\x9a\xa1\xb7\xae\xfa\x1d\xdc\x00\xa2\x0d\xbd\xf1\x56\xb0\xc2\xcb\xb4\x4a\xc1\x9d\xba\x35\x51\x3d\xd6\xe8\xed\x18\xcb\x78\xf4\xaf\x95\xcf\x5b\xa9\x8e\x58\x89\xdf\xe8\x9c\x7c\x3a\x9b\xe1\xfc\x33\xb9\x57\x55\x4b\x3f\xa9\xcf\xa5\xd3\x04\xff\x33\xcf\xd7\xb6\xb7\x1c\x8a\xef\xac\xdc\x92\x0c\xe0\xa3\x24\x5c\xf8\x5c\x1f\xcc\xd3\x48\x59\xa1\x96\xae\xc6\xea\x73\x6e\xe9\xf3\x61\x32\x1d\xde\x9f\x09\xae\xeb\x2d\x37\x86\x55\x84\x1a\x53\x0b\xad\x6d\x6d\xa2\x53\xb4\x80\x25\x24\x4c\xb7\xf8\xc4\x2a\x86\xfc\xb4\x57\x5b\xb8\x64\xea\x50\x8b\xa5\x04\x50\xad\xf1\x52\x05\x57\xb9\xfe\x78\xa8\xba\x93\xf1\x35\x7a\xad\x82\xbe\xff\xa7\x36\x92\xbb\xc6\xcf\xbe\x91\x82\x3d\x90\x03\xe8\xea\xaf\x48\x06\xcb\xb5\x07\x4a\x96\x0e\xa1\xd4\x33\x5e\xdc\x67\xff\x02\x00\x00\xff\xff\x39\x9f\x6b\x23\xd9\x06\x00\x00") +var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x8e\xdb\x20\x10\xbd\xfb\x2b\xe6\xb8\x91\x72\xe8\x7d\x4f\x4e\x96\x54\x96\x10\x6e\x13\x22\xe5\x86\x28\x9e\x5d\xa3\xb8\x78\x0b\x78\xd5\xfc\x7d\x85\x9d\x78\x93\xc5\xc5\x55\xba\x17\x4b\x1e\x1e\xa3\x79\x6f\x1e\x6f\xbd\x25\x39\x27\xc0\xf3\x15\x25\x50\x6c\x80\x95\x1c\xc8\xa1\xd8\xf1\x1d\xbc\x76\xae\x16\xa6\xf5\xfa\x59\x2b\xe9\x75\x6b\x84\x6a\x34\x1a\x2f\x1c\xda\x37\xb4\x0e\x1e\x32\x80\xd7\xee\x47\xa3\x95\x38\xe2\x09\x56\xb4\x5c\xf5\xf7\xd9\x9e\xd2\x65\x06\x60\xf1\x45\x3b\x8f\x16\x2b\x58\x95\x25\x25\x39\x83\x27\xb2\xc9\xf7\x94\xc3\x26\xa7\x3b\x72\x8b\x11\xd2\x43\xc1\xf8\xd8\x61\xc4\x7e\x09\xb8\x46\x3a\x2f\x2c\x7a\xab\xe7\x90\x01\x74\x12\xaa\xed\x4c\x0a\x25\x95\x42\xe7\x84\x6f\x8f\x68\x80\x93\x03\x0f\xc5\x3d\x2b\xbe\xef\xc9\xc3\x3b\xa7\x05\x94\x0c\xd6\x25\xdb\xd0\x62\xcd\x61\x4b\xbe\xd1\x7c\x4d\xb2\xc5\x63\x96\xdd\xa3\xdb\xaf\x0e\xad\xc6\x79\xdd\x06\x5c\x44\xf3\x72\x74\x12\xba\x8a\x2f\x45\xb3\x2f\x2f\xd8\xcf\x25\xa1\xcd\x73\x3b\xcb\x60\x70\x88\x48\x41\xb4\x71\x5e\x36\xcd\xd0\x5b\x57\xfd\x0e\x6e\x00\xd1\x86\x3e\x78\x2b\x58\xe1\x6d\x5a\xa5\xe0\x4e\xdd\x9a\xa8\x1e\x6b\xf4\x71\x8c\x65\x3c\xfa\xe7\xca\xe7\xad\x54\x47\xac\xc4\x4f\x74\x4e\xbe\x9c\xcd\x70\xfe\x99\xdc\xab\xaa\xa5\x9f\xd4\xe7\xd2\x69\x82\xff\x99\xe7\x7b\xdb\x5b\x0e\xc5\x57\x56\x6e\x49\x06\x70\x2f\x09\x17\x3e\xd7\x07\xf3\x34\x52\x56\xa8\xa5\xab\xb1\xfa\x3f\xb7\xf4\xf9\x30\x99\x0e\xff\x9e\x09\xae\xeb\x2d\x37\x86\x55\x84\x1a\x53\x0b\xad\x6d\x6d\xa2\x53\xb4\x80\x25\x24\x4c\x37\xed\xb0\xfb\xd7\x33\x64\xaa\xbd\xda\xcc\x25\x67\x87\x5a\x2c\x2f\x80\x6a\x8d\x97\x2a\x38\xcd\xf5\xc7\x43\xd5\x9d\x8c\xaf\xd1\x6b\x15\x34\xff\x3b\xdd\x91\xf0\x35\x7e\xf6\xdd\x14\xec\x89\x1c\x40\x57\xbf\x45\x32\x6c\xae\x7d\x51\xb2\x74\x30\xa5\x9e\xf6\xe2\x31\xfb\x13\x00\x00\xff\xff\xfb\x06\xc2\x3d\xed\x06\x00\x00") func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) { return bindataRead( @@ -106,8 +106,8 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1753, mode: os.FileMode(0644), modTime: time.Unix(1595240420, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x28, 0xbf, 0x64, 0xe0, 0x65, 0x53, 0xd3, 0x80, 0xf4, 0x46, 0xce, 0xd6, 0x23, 0x4e, 0xc5, 0x8f, 0x80, 0x4e, 0x91, 0xa7, 0x2e, 0x9, 0x3b, 0xf4, 0x5f, 0xa1, 0xff, 0xfc, 0x6e, 0x4, 0xa2, 0xe7}} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1773, mode: os.FileMode(0644), modTime: time.Unix(1595256491, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x1e, 0x5, 0x35, 0x9, 0xb2, 0x2d, 0x6f, 0x33, 0x63, 0xa2, 0x7a, 0x5b, 0xd2, 0x2d, 0xcb, 0x79, 0x7e, 0x6, 0xb4, 0x9d, 0x35, 0xd8, 0x9b, 0x55, 0xe5, 0xf8, 0x44, 0xca, 0xa6, 0xf3, 0xd3}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql index 1af27e9de..5fa9c7a29 100644 --- a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql +++ b/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql @@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications ( retry_count INT NOT NULL DEFAULT 0, success BOOLEAN NOT NULL DEFAULT FALSE, error INT NOT NULL DEFAULT 0, - UNIQUE(message_id, public_key, installation_id) + UNIQUE(message_id, public_key, installation_id) ON CONFLICT REPLACE ); CREATE TABLE IF NOT EXISTS push_notification_client_registrations ( diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index fc39a5df5..0c7396b4f 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -273,6 +273,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { } return nil }) + s.Require().NoError(err) } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { @@ -400,6 +401,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO return nil }) + s.Require().NoError(err) } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { @@ -476,13 +478,39 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) s.Require().NoError(alice.SaveChat(&chat)) inputMessage := buildTestMessage(chat) - response, err := alice.SendChatMessage(context.Background(), inputMessage) + _, err = alice.SendChatMessage(context.Background(), inputMessage) s.Require().NoError(err) - messageIDString := response.Messages[0].ID - messageID, err := hex.DecodeString(messageIDString[2:]) + // We check that alice retrieves the info from the server + var info []*push_notification_client.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for bob + if len(info) != 1 { + return errors.New("info not fetched") + } + return nil + + }) s.Require().NoError(err) + s.Require().NotNil(info) + s.Require().Equal(bob.installationID, info[0].InstallationID) + s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken) + s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey) + // The message has been sent, but not received, now we remove a contact so that the token is invalidated frankContact = &Contact{ ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)), @@ -518,7 +546,14 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { // Make sure access token is not the same s.Require().NotEqual(newBobServers[0].AccessToken, bobServers[0].AccessToken) - var info []*push_notification_client.PushNotificationInfo + // Send another message, here the token will not be valid + inputMessage = buildTestMessage(chat) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) + s.Require().NoError(err) + err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -537,6 +572,9 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { if len(info) != 1 { return errors.New("info not fetched") } + if newBobServers[0].AccessToken != info[0].AccessToken { + return errors.New("still using the old access token") + } return nil }) @@ -574,4 +612,6 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { } return nil }) + + s.Require().NoError(err) } From 63af6aa79b935bc6f28f8e62ec7dd4ac869f6350 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Tue, 21 Jul 2020 17:41:10 +0200 Subject: [PATCH 44/46] Use ephemeral keys for sending messsages --- protocol/common/message_processor.go | 109 ++++++++++++++---- protocol/common/raw_message.go | 2 + protocol/messenger.go | 3 +- protocol/messenger_contact_update_test.go | 18 +-- protocol/messenger_installations_test.go | 18 +-- protocol/messenger_mute_test.go | 18 +-- protocol/messenger_test.go | 26 ++--- protocol/push_notification_client/client.go | 34 +++++- protocol/push_notification_server/server.go | 10 +- protocol/push_notification_test.go | 37 ++++-- protocol/transport/filters_manager.go | 16 +-- protocol/transport/transport.go | 1 + protocol/transport/waku/waku_service.go | 61 +--------- protocol/transport/whisper/whisper_service.go | 6 +- protocol/v1/status_message.go | 6 +- 15 files changed, 216 insertions(+), 149 deletions(-) diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 5c4720fc4..3416472ad 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -4,6 +4,7 @@ import ( "context" "crypto/ecdsa" "database/sql" + "sync" "time" "github.com/golang/protobuf/proto" @@ -42,11 +43,13 @@ type SentMessage struct { } type MessageProcessor struct { - identity *ecdsa.PrivateKey - datasync *datasync.DataSync - protocol *encryption.Protocol - transport transport.Transport - logger *zap.Logger + identity *ecdsa.PrivateKey + datasync *datasync.DataSync + protocol *encryption.Protocol + transport transport.Transport + logger *zap.Logger + ephemeralKeys map[string]*ecdsa.PrivateKey + mutex sync.Mutex subscriptions []chan<- *SentMessage @@ -76,12 +79,13 @@ func NewMessageProcessor( ds := datasync.New(dataSyncNode, dataSyncTransport, features.Datasync, logger) p := &MessageProcessor{ - identity: identity, - datasync: ds, - protocol: enc, - transport: transport, - logger: logger, - featureFlags: features, + identity: identity, + datasync: ds, + protocol: enc, + transport: transport, + logger: logger, + ephemeralKeys: make(map[string]*ecdsa.PrivateKey), + featureFlags: features, } // Initializing DataSync is required to encrypt and send messages. @@ -111,7 +115,7 @@ func (p *MessageProcessor) SendPrivate( ) ([]byte, error) { p.logger.Debug( "sending a private message", - zap.Binary("public-key", crypto.FromECDSAPub(recipient)), + zap.String("public-key", types.EncodeHex(crypto.FromECDSAPub(recipient))), zap.String("site", "SendPrivate"), ) return p.sendPrivate(ctx, recipient, rawMessage) @@ -128,12 +132,15 @@ func (p *MessageProcessor) SendGroup( "sending a private group message", zap.String("site", "SendGroup"), ) + if rawMessage.Sender == nil { + rawMessage.Sender = p.identity + } // Calculate messageID first wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } - messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) + messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) // Send to each recipients for _, recipient := range recipients { @@ -151,24 +158,42 @@ func (p *MessageProcessor) sendPrivate( recipient *ecdsa.PublicKey, rawMessage *RawMessage, ) ([]byte, error) { - p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient))) + p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient)))) + + if rawMessage.ResendAutomatically && (rawMessage.Sender != nil || rawMessage.SkipNegotiation) { + return nil, errors.New("setting identity, skip-negotiation and datasync not supported") + } + + // If we use our own key we don't skip negotiation + if rawMessage.Sender == nil { + rawMessage.Sender = p.identity + } wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } - messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) + messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) if p.featureFlags.Datasync && rawMessage.ResendAutomatically { + // No need to call transport tracking. + // It is done in a data sync dispatch step. if err := p.addToDataSync(recipient, wrappedMessage); err != nil { return nil, errors.Wrap(err, "failed to send message with datasync") } - // No need to call transport tracking. - // It is done in a data sync dispatch step. + } else if rawMessage.SkipNegotiation { + messageIDs := [][]byte{messageID} + hash, newMessage, err := p.sendRawMessage(ctx, recipient, wrappedMessage, messageIDs) + if err != nil { + return nil, errors.Wrap(err, "failed to send a message spec") + } + + p.transport.Track(messageIDs, hash, newMessage) + } else { - messageSpec, err := p.protocol.BuildDirectMessage(p.identity, recipient, wrappedMessage) + messageSpec, err := p.protocol.BuildDirectMessage(rawMessage.Sender, recipient, wrappedMessage) if err != nil { return nil, errors.Wrap(err, "failed to encrypt message") } @@ -191,7 +216,7 @@ func (p *MessageProcessor) SendPairInstallation( recipient *ecdsa.PublicKey, rawMessage *RawMessage, ) ([]byte, error) { - p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient))) + p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient)))) wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { @@ -243,6 +268,9 @@ func (p *MessageProcessor) SendPublic( rawMessage *RawMessage, ) ([]byte, error) { var newMessage *types.NewMessage + if rawMessage.Sender == nil { + rawMessage.Sender = p.identity + } wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { @@ -261,7 +289,7 @@ func (p *MessageProcessor) SendPublic( return nil, err } - messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage) + messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) p.transport.Track([][]byte{messageID}, hash, newMessage) @@ -314,8 +342,20 @@ func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, application func (p *MessageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error { logger := p.logger.With(zap.String("site", "handleEncryptionLayer")) publicKey := message.SigPubKey() + destination := message.Dst - err := message.HandleEncryption(p.identity, publicKey, p.protocol) + destinationID := types.EncodeHex(crypto.FromECDSAPub(destination)) + p.mutex.Lock() + decryptionKey, ok := p.ephemeralKeys[destinationID] + p.mutex.Unlock() + logger.Info("destination id", zap.String("desti", destinationID)) + skipNegotiation := true + if !ok { + skipNegotiation = false + decryptionKey = p.identity + } + + err := message.HandleEncryption(decryptionKey, publicKey, p.protocol, skipNegotiation) if err == encryption.ErrDeviceNotFound { if err := p.handleErrDeviceNotFound(ctx, publicKey); err != nil { logger.Error("failed to handle ErrDeviceNotFound", zap.Error(err)) @@ -358,7 +398,7 @@ func (p *MessageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe } func (p *MessageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) { - wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, p.identity) + wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, rawMessage.Sender) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } @@ -409,6 +449,24 @@ func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu return nil } +// sendRawMessage sends a message not wrapped in an encryption layer +func (p *MessageProcessor) sendRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) { + newMessage := &types.NewMessage{ + TTL: whisperTTL, + Payload: payload, + PowTarget: calculatePoW(payload), + PowTime: whisperPoWTime, + } + + hash, err := p.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey) + if err != nil { + return nil, nil, err + } + + return hash, newMessage, nil + +} + // sendMessageSpec analyses the spec properties and selects a proper transport method. func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) { newMessage, err := MessageSpecToWhisper(messageSpec) @@ -462,6 +520,13 @@ func (p *MessageProcessor) JoinPublic(chatID string) error { return p.transport.JoinPublic(chatID) } +func (p *MessageProcessor) LoadKeyFilters(privateKey *ecdsa.PrivateKey) (*transport.Filter, error) { + p.mutex.Lock() + p.ephemeralKeys[types.EncodeHex(crypto.FromECDSAPub(&privateKey.PublicKey))] = privateKey + p.mutex.Unlock() + return p.transport.LoadKeyFilters(privateKey) +} + func MessageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) { var newMessage *types.NewMessage diff --git a/protocol/common/raw_message.go b/protocol/common/raw_message.go index d21259a48..573810a2f 100644 --- a/protocol/common/raw_message.go +++ b/protocol/common/raw_message.go @@ -15,7 +15,9 @@ type RawMessage struct { SendCount int Sent bool ResendAutomatically bool + SkipNegotiation bool MessageType protobuf.ApplicationMetadataMessage_Type Payload []byte + Sender *ecdsa.PrivateKey Recipients []*ecdsa.PublicKey } diff --git a/protocol/messenger.go b/protocol/messenger.go index db1297f76..1e15f68aa 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1379,6 +1379,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } logger := m.logger.With(zap.String("site", "Send"), zap.String("chatID", message.ChatId)) + logger.Info("SENDING CHAT MESSAGE") var response MessengerResponse // A valid added chat is required. @@ -1426,6 +1427,7 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes return nil, errors.New("chat type not supported") } + // THERE'S A RACE CONDITION, WE SHOULD CALCULATE AND TRACK THE ID FIRST id, err := m.dispatchMessage(ctx, &common.RawMessage{ LocalChatID: chat.ID, Payload: encodedMessage, @@ -1743,7 +1745,6 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { // RetrieveAll retrieves messages from all filters, processes them and returns a // MessengerResponse to the client func (m *Messenger) RetrieveAll() (*MessengerResponse, error) { - m.logger.Info("RETRIEVING ALL", zap.String("installation-id", m.installationID)) chatWithMessages, err := m.transport.RetrieveRawAll() if err != nil { return nil, err diff --git a/protocol/messenger_contact_update_test.go b/protocol/messenger_contact_update_test.go index 0b291e6e1..25a6e9de7 100644 --- a/protocol/messenger_contact_update_test.go +++ b/protocol/messenger_contact_update_test.go @@ -15,7 +15,7 @@ import ( "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/tt" - "github.com/status-im/status-go/whisper/v6" + "github.com/status-im/status-go/waku" ) func TestMessengerContactUpdateSuite(t *testing.T) { @@ -27,8 +27,8 @@ type MessengerContactUpdateSuite struct { m *Messenger // main instance of Messenger privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, - // a single Whisper service should be shared. - shh types.Whisper + // a single waku service should be shared. + shh types.Waku tmpFiles []*os.File // files to clean up logger *zap.Logger } @@ -36,17 +36,17 @@ type MessengerContactUpdateSuite struct { func (s *MessengerContactUpdateSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() - config := whisper.DefaultConfig - config.MinimumAcceptedPOW = 0 - shh := whisper.New(&config) - s.shh = gethbridge.NewGethWhisperWrapper(shh) + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start(nil)) s.m = s.newMessenger(s.shh) s.privateKey = s.m.identity } -func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -72,7 +72,7 @@ func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, pri return m } -func (s *MessengerContactUpdateSuite) newMessenger(shh types.Whisper) *Messenger { +func (s *MessengerContactUpdateSuite) newMessenger(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) diff --git a/protocol/messenger_installations_test.go b/protocol/messenger_installations_test.go index 514bafa37..45a1a4258 100644 --- a/protocol/messenger_installations_test.go +++ b/protocol/messenger_installations_test.go @@ -17,7 +17,7 @@ import ( "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/tt" - "github.com/status-im/status-go/whisper/v6" + "github.com/status-im/status-go/waku" ) func TestMessengerInstallationSuite(t *testing.T) { @@ -30,8 +30,8 @@ type MessengerInstallationSuite struct { privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, - // a single Whisper service should be shared. - shh types.Whisper + // a single Waku service should be shared. + shh types.Waku tmpFiles []*os.File // files to clean up logger *zap.Logger @@ -40,17 +40,17 @@ type MessengerInstallationSuite struct { func (s *MessengerInstallationSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() - config := whisper.DefaultConfig - config.MinimumAcceptedPOW = 0 - shh := whisper.New(&config) - s.shh = gethbridge.NewGethWhisperWrapper(shh) + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start(nil)) s.m = s.newMessenger(s.shh) s.privateKey = s.m.identity } -func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -77,7 +77,7 @@ func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, priv return m } -func (s *MessengerInstallationSuite) newMessenger(shh types.Whisper) *Messenger { +func (s *MessengerInstallationSuite) newMessenger(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) diff --git a/protocol/messenger_mute_test.go b/protocol/messenger_mute_test.go index fe3fe8b7b..8fd525771 100644 --- a/protocol/messenger_mute_test.go +++ b/protocol/messenger_mute_test.go @@ -14,7 +14,7 @@ import ( "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/tt" - "github.com/status-im/status-go/whisper/v6" + "github.com/status-im/status-go/waku" ) func TestMessengerMuteSuite(t *testing.T) { @@ -27,8 +27,8 @@ type MessengerMuteSuite struct { privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, - // a single Whisper service should be shared. - shh types.Whisper + // a single Waku service should be shared. + shh types.Waku tmpFiles []*os.File // files to clean up logger *zap.Logger @@ -37,17 +37,17 @@ type MessengerMuteSuite struct { func (s *MessengerMuteSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() - config := whisper.DefaultConfig - config.MinimumAcceptedPOW = 0 - shh := whisper.New(&config) - s.shh = gethbridge.NewGethWhisperWrapper(shh) + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start(nil)) s.m = s.newMessenger(s.shh) s.privateKey = s.m.identity } -func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -74,7 +74,7 @@ func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Whisper, privateKey * return m } -func (s *MessengerMuteSuite) newMessenger(shh types.Whisper) *Messenger { +func (s *MessengerMuteSuite) newMessenger(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) diff --git a/protocol/messenger_test.go b/protocol/messenger_test.go index d0b3aa23a..3eb834e52 100644 --- a/protocol/messenger_test.go +++ b/protocol/messenger_test.go @@ -27,7 +27,7 @@ import ( "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/tt" v1protocol "github.com/status-im/status-go/protocol/v1" - "github.com/status-im/status-go/whisper/v6" + "github.com/status-im/status-go/waku" ) const ( @@ -62,13 +62,13 @@ type MessengerSuite struct { privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, // a single Whisper service should be shared. - shh types.Whisper + shh types.Waku tmpFiles []*os.File // files to clean up logger *zap.Logger } type testNode struct { - shh types.Whisper + shh types.Waku } func (n *testNode) NewENSVerifier(_ *zap.Logger) enstypes.ENSVerifier { @@ -84,27 +84,27 @@ func (n *testNode) RemovePeer(_ string) error { } func (n *testNode) GetWaku(_ interface{}) (types.Waku, error) { - panic("not implemented") + return n.shh, nil } func (n *testNode) GetWhisper(_ interface{}) (types.Whisper, error) { - return n.shh, nil + return nil, nil } func (s *MessengerSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() - config := whisper.DefaultConfig - config.MinimumAcceptedPOW = 0 - shh := whisper.New(&config) - s.shh = gethbridge.NewGethWhisperWrapper(shh) + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start(nil)) s.m = s.newMessenger(s.shh) s.privateKey = s.m.identity } -func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -132,7 +132,7 @@ func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecds return m } -func (s *MessengerSuite) newMessenger(shh types.Whisper) *Messenger { +func (s *MessengerSuite) newMessenger(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) return s.newMessengerWithKey(shh, privateKey) @@ -2056,7 +2056,7 @@ type MockEthClient struct { } type mockSendMessagesRequest struct { - types.Whisper + types.Waku req types.MessagesRequest } @@ -2101,7 +2101,7 @@ func (s *MessengerSuite) TestMessageJSON() { func (s *MessengerSuite) TestRequestHistoricMessagesRequest() { shh := &mockSendMessagesRequest{ - Whisper: s.shh, + Waku: s.shh, } m := s.newMessenger(shh) ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) diff --git a/protocol/push_notification_client/client.go b/protocol/push_notification_client/client.go index bdc99c8b7..08d9f0ce4 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/push_notification_client/client.go @@ -288,6 +288,7 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { // Nothing to do if len(trackedMessageIDs) == 0 { + c.config.Logger.Info("nothing to do") return nil } @@ -611,6 +612,14 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] } c.config.Logger.Info("actionable info", zap.Int("count", len(actionableInfos))) + ephemeralKey, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + _, err = c.messageProcessor.LoadKeyFilters(ephemeralKey) + if err != nil { + return nil, err + } var actionedInfo []*PushNotificationInfo for _, infos := range actionableInfos { @@ -636,8 +645,10 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] } rawMessage := &common.RawMessage{ - Payload: payload, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, + Payload: payload, + Sender: ephemeralKey, + SkipNegotiation: true, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, } // TODO: We should use the messageID for the response @@ -1076,11 +1087,24 @@ func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { return err } - rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY, + ephemeralKey, err := crypto.GenerateKey() + if err != nil { + return err } + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + Sender: ephemeralKey, + SkipNegotiation: true, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY, + } + + filter, err := c.messageProcessor.LoadKeyFilters(ephemeralKey) + if err != nil { + return err + } + c.config.Logger.Debug("Filter", zap.String("filter-id", filter.FilterID)) + encodedPublicKey := hex.EncodeToString(hashedPublicKey) c.config.Logger.Info("sending query") messageID, err := c.messageProcessor.SendPublic(context.Background(), encodedPublicKey, rawMessage) diff --git a/protocol/push_notification_server/server.go b/protocol/push_notification_server/server.go index 9ec2fc12f..bd84511d6 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/push_notification_server/server.go @@ -360,8 +360,9 @@ func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, messag } rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE, + SkipNegotiation: true, } _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) @@ -382,8 +383,9 @@ func (s *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, } rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, + SkipNegotiation: true, } _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 0c7396b4f..1bf46cc22 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -20,7 +20,7 @@ import ( "github.com/status-im/status-go/protocol/push_notification_client" "github.com/status-im/status-go/protocol/push_notification_server" "github.com/status-im/status-go/protocol/tt" - "github.com/status-im/status-go/whisper/v6" + "github.com/status-im/status-go/waku" ) func TestMessengerPushNotificationSuite(t *testing.T) { @@ -32,8 +32,8 @@ type MessengerPushNotificationSuite struct { m *Messenger // main instance of Messenger privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger // If one wants to send messages between different instances of Messenger, - // a single Whisper service should be shared. - shh types.Whisper + // a single Waku service should be shared. + shh types.Waku tmpFiles []*os.File // files to clean up logger *zap.Logger } @@ -41,17 +41,25 @@ type MessengerPushNotificationSuite struct { func (s *MessengerPushNotificationSuite) SetupTest() { s.logger = tt.MustCreateTestLogger() - config := whisper.DefaultConfig - config.MinimumAcceptedPOW = 0 - shh := whisper.New(&config) - s.shh = gethbridge.NewGethWhisperWrapper(shh) + config := waku.DefaultConfig + config.MinimumAcceptedPoW = 0 + shh := waku.New(&config, s.logger) + s.shh = gethbridge.NewGethWakuWrapper(shh) s.Require().NoError(shh.Start(nil)) s.m = s.newMessenger(s.shh) s.privateKey = s.m.identity } -func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Whisper, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { +func (s *MessengerPushNotificationSuite) TearDownTest() { + s.Require().NoError(s.m.Shutdown()) + for _, f := range s.tmpFiles { + _ = os.Remove(f.Name()) + } + _ = s.logger.Sync() +} + +func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -71,7 +79,7 @@ func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Whisp return m } -func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger { +func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) @@ -84,14 +92,14 @@ func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Whisper, return s.newMessengerWithOptions(shh, privateKey, options) } -func (s *MessengerPushNotificationSuite) newMessenger(shh types.Whisper) *Messenger { +func (s *MessengerPushNotificationSuite) newMessenger(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) return s.newMessengerWithKey(s.shh, privateKey) } -func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Whisper) *Messenger { +func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku) *Messenger { privateKey, err := crypto.GenerateKey() s.Require().NoError(err) @@ -274,6 +282,9 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { return nil }) s.Require().NoError(err) + s.Require().NoError(bob2.Shutdown()) + s.Require().NoError(alice.Shutdown()) + s.Require().NoError(server.Shutdown()) } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { @@ -402,6 +413,8 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO }) s.Require().NoError(err) + s.Require().NoError(alice.Shutdown()) + s.Require().NoError(server.Shutdown()) } func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { @@ -614,4 +627,6 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { }) s.Require().NoError(err) + s.Require().NoError(alice.Shutdown()) + s.Require().NoError(server.Shutdown()) } diff --git a/protocol/transport/filters_manager.go b/protocol/transport/filters_manager.go index 7f49a8175..3e3da4b38 100644 --- a/protocol/transport/filters_manager.go +++ b/protocol/transport/filters_manager.go @@ -218,15 +218,15 @@ func (s *FiltersManager) Remove(filters ...*Filter) error { } // LoadPartitioned creates a filter for a partitioned topic. -func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey) (*Filter, error) { - return s.loadPartitioned(publicKey, false) +func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) { + return s.loadPartitioned(publicKey, identity, listen) } func (s *FiltersManager) loadMyPartitioned() (*Filter, error) { - return s.loadPartitioned(&s.privateKey.PublicKey, true) + return s.loadPartitioned(&s.privateKey.PublicKey, s.privateKey, true) } -func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool) (*Filter, error) { +func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) { s.mutex.Lock() defer s.mutex.Unlock() @@ -237,7 +237,7 @@ func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool // We set up a filter so we can publish, // but we discard envelopes if listen is false. - filter, err := s.addAsymmetric(chatID, listen) + filter, err := s.addAsymmetric(chatID, identity, listen) if err != nil { return nil, err } @@ -321,7 +321,7 @@ func (s *FiltersManager) LoadDiscovery() ([]*Filter, error) { OneToOne: true, } - discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, true) + discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, s.privateKey, true) if err != nil { return nil, err } @@ -439,7 +439,7 @@ func (s *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) { // addAsymmetricFilter adds a filter with our private key // and set minPow according to the listen parameter. -func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter, error) { +func (s *FiltersManager) addAsymmetric(chatID string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) { var ( err error pow = 1.0 // use PoW high enough to discard all messages for the filter @@ -452,7 +452,7 @@ func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter, topic := ToTopic(chatID) topics := [][]byte{topic} - privateKeyID, err := s.service.AddKeyPair(s.privateKey) + privateKeyID, err := s.service.AddKeyPair(identity) if err != nil { return nil, err } diff --git a/protocol/transport/transport.go b/protocol/transport/transport.go index 6a75fe546..1d804c62e 100644 --- a/protocol/transport/transport.go +++ b/protocol/transport/transport.go @@ -35,6 +35,7 @@ type Transport interface { RemoveFilters(filters []*Filter) error ResetFilters() error Filters() []*Filter + LoadKeyFilters(*ecdsa.PrivateKey) (*Filter, error) ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Filter, error) RetrieveRawAll() (map[Filter][]*types.Message, error) } diff --git a/protocol/transport/waku/waku_service.go b/protocol/transport/waku/waku_service.go index ec4041e7a..c184a245d 100644 --- a/protocol/transport/waku/waku_service.go +++ b/protocol/transport/waku/waku_service.go @@ -204,61 +204,6 @@ func (a *Transport) LeaveGroup(publicKeys []*ecdsa.PublicKey) error { return nil } -type Message struct { - Message *types.Message - Public bool -} - -func (a *Transport) RetrieveAllMessages() ([]Message, error) { - var messages []Message - - for _, filter := range a.filters.Filters() { - filterMsgs, err := a.api.GetFilterMessages(filter.FilterID) - if err != nil { - return nil, err - } - - for _, m := range filterMsgs { - messages = append(messages, Message{ - Message: m, - Public: filter.IsPublic(), - }) - } - } - - return messages, nil -} - -func (a *Transport) RetrievePublicMessages(chatID string) ([]*types.Message, error) { - filter, err := a.filters.LoadPublic(chatID) - if err != nil { - return nil, err - } - - return a.api.GetFilterMessages(filter.FilterID) -} - -func (a *Transport) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*types.Message, error) { - chats := a.filters.FiltersByPublicKey(publicKey) - discoveryChats, err := a.filters.Init(nil, nil) - if err != nil { - return nil, err - } - - var result []*types.Message - - for _, chat := range append(chats, discoveryChats...) { - filterMsgs, err := a.api.GetFilterMessages(chat.FilterID) - if err != nil { - return nil, err - } - - result = append(result, filterMsgs...) - } - - return result, nil -} - func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) { result := make(map[transport.Filter][]*types.Message) @@ -318,7 +263,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage * return nil, err } - filter, err := a.filters.LoadPartitioned(publicKey) + filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false) if err != nil { return nil, err } @@ -329,6 +274,10 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage * return a.api.Post(ctx, *newMessage) } +func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) { + return a.filters.LoadPartitioned(&key.PublicKey, key, true) +} + func (a *Transport) SendPrivateOnDiscovery(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) { if err := a.addSig(newMessage); err != nil { return nil, err diff --git a/protocol/transport/whisper/whisper_service.go b/protocol/transport/whisper/whisper_service.go index f5c1c2382..fd043eb8b 100644 --- a/protocol/transport/whisper/whisper_service.go +++ b/protocol/transport/whisper/whisper_service.go @@ -318,7 +318,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage * return nil, err } - filter, err := a.filters.LoadPartitioned(publicKey) + filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false) if err != nil { return nil, err } @@ -406,6 +406,10 @@ func (a *Transport) SendMessagesRequest( return } +func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) { + return a.filters.LoadPartitioned(&key.PublicKey, key, true) +} + func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) { for { select { diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index 734863b62..1c948dd9b 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -101,10 +101,14 @@ func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error { return nil } -func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol) error { +func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol, skipNegotiation bool) error { // As we handle non-encrypted messages, we make sure that DecryptPayload // is set regardless of whether this step is successful m.DecryptedPayload = m.TransportPayload + // Nothing to do + if skipNegotiation { + return nil + } var protocolMessage encryption.ProtocolMessage err := proto.Unmarshal(m.TransportPayload, &protocolMessage) From 4b8739a8bc33866c8fbe88e0155dc9734205590f Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Wed, 22 Jul 2020 09:41:40 +0200 Subject: [PATCH 45/46] Polish up and address review feedback --- appdatabase/migrations/bindata.go | 20 +- .../sql/0008_add_push_notifications.up.sql | 8 +- multiaccounts/accounts/database.go | 107 +- protocol/common/crypto.go | 6 +- protocol/common/message_processor.go | 161 ++- protocol/common/message_processor_test.go | 40 +- protocol/common/raw_message.go | 23 +- protocol/message_handler.go | 2 +- protocol/messenger.go | 89 +- protocol/messenger_config.go | 16 +- protocol/messenger_installations_test.go | 10 +- protocol/migrations/migrations.go | 90 +- .../sqlite/1593087212_add_mute_chat.up.sql | 1 - ...mute_chat_and_raw_message_fields.down.sql} | 0 ...dd_mute_chat_and_raw_message_fields.up.sql | 3 + protocol/persistence.go | 12 +- protocol/protobuf/push_notifications.pb.go | 223 ++--- protocol/protobuf/push_notifications.proto | 15 +- protocol/push_notification_test.go | 235 ++++- .../client.go | 945 ++++++++++-------- .../client_test.go | 32 +- .../migrations/migrations.go | 6 +- .../sql/1593601729_initial_schema.down.sql | 0 .../sql/1593601729_initial_schema.up.sql | 0 .../migrations/sql/doc.go | 0 .../persistence.go | 14 +- .../persistence_test.go | 37 +- .../errors.go | 2 +- .../gorush.go | 5 +- .../gorush_test.go | 14 +- .../migrations/migrations.go | 6 +- .../sql/1593601728_initial_schema.down.sql | 0 .../sql/1593601728_initial_schema.up.sql | 0 .../migrations/sql/doc.go | 0 .../persistence.go | 4 +- .../persistence_test.go | 4 +- .../server.go | 329 +++--- .../server_test.go | 115 +-- protocol/sqlite/migrations.go | 4 +- services/ext/api.go | 37 +- services/ext/service.go | 11 +- services/shhext/api_geth_test.go | 6 +- services/wakuext/api_test.go | 6 +- static/bindata.go | 8 +- 44 files changed, 1478 insertions(+), 1168 deletions(-) delete mode 100644 protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql rename protocol/migrations/sqlite/{1593087212_add_mute_chat.down.sql => 1593087212_add_mute_chat_and_raw_message_fields.down.sql} (100%) create mode 100644 protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.up.sql rename protocol/{push_notification_client => pushnotificationclient}/client.go (70%) rename protocol/{push_notification_client => pushnotificationclient}/client_test.go (90%) rename protocol/{push_notification_client => pushnotificationclient}/migrations/migrations.go (98%) rename protocol/{push_notification_client => pushnotificationclient}/migrations/sql/1593601729_initial_schema.down.sql (100%) rename protocol/{push_notification_client => pushnotificationclient}/migrations/sql/1593601729_initial_schema.up.sql (100%) rename protocol/{push_notification_client => pushnotificationclient}/migrations/sql/doc.go (100%) rename protocol/{push_notification_client => pushnotificationclient}/persistence.go (98%) rename protocol/{push_notification_client => pushnotificationclient}/persistence_test.go (94%) rename protocol/{push_notification_server => pushnotificationserver}/errors.go (96%) rename protocol/{push_notification_server => pushnotificationserver}/gorush.go (96%) rename protocol/{push_notification_server => pushnotificationserver}/gorush_test.go (89%) rename protocol/{push_notification_server => pushnotificationserver}/migrations/migrations.go (98%) rename protocol/{push_notification_server => pushnotificationserver}/migrations/sql/1593601728_initial_schema.down.sql (100%) rename protocol/{push_notification_server => pushnotificationserver}/migrations/sql/1593601728_initial_schema.up.sql (100%) rename protocol/{push_notification_server => pushnotificationserver}/migrations/sql/doc.go (100%) rename protocol/{push_notification_server => pushnotificationserver}/persistence.go (98%) rename protocol/{push_notification_server => pushnotificationserver}/persistence_test.go (96%) rename protocol/{push_notification_server => pushnotificationserver}/server.go (76%) rename protocol/{push_notification_server => pushnotificationserver}/server_test.go (82%) diff --git a/appdatabase/migrations/bindata.go b/appdatabase/migrations/bindata.go index 7af13be2b..d1a9cd1df 100644 --- a/appdatabase/migrations/bindata.go +++ b/appdatabase/migrations/bindata.go @@ -12,7 +12,7 @@ // 0005_waku_mode.up.sql (146B) // 0006_appearance.up.sql (67B) // 0007_enable_waku_default.up.sql (38B) -// 0008_add_push_notifications.up.sql (294B) +// 0008_add_push_notifications.up.sql (349B) // doc.go (74B) package migrations @@ -217,7 +217,7 @@ func _0004_pending_stickersDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -237,7 +237,7 @@ func _0004_pending_stickersUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3c, 0xed, 0x25, 0xdf, 0x75, 0x2, 0x6c, 0xf0, 0xa2, 0xa8, 0x37, 0x62, 0x65, 0xad, 0xfd, 0x98, 0xa0, 0x9d, 0x63, 0x94, 0xdf, 0x6b, 0x46, 0xe0, 0x68, 0xec, 0x9c, 0x7f, 0x77, 0xdd, 0xb3, 0x6}} return a, nil } @@ -257,7 +257,7 @@ func _0005_waku_modeDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -277,7 +277,7 @@ func _0005_waku_modeUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x91, 0xc, 0xd7, 0x89, 0x61, 0x2e, 0x4c, 0x5a, 0xb6, 0x67, 0xd1, 0xc1, 0x42, 0x24, 0x38, 0xd6, 0x1b, 0x75, 0x41, 0x9c, 0x23, 0xb0, 0xca, 0x5c, 0xf1, 0x5c, 0xd0, 0x13, 0x92, 0x3e, 0xe1}} return a, nil } @@ -297,7 +297,7 @@ func _0006_appearanceUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1585895847, 0)} + info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0x6, 0x25, 0x6c, 0xe4, 0x9d, 0xa7, 0x72, 0xe8, 0xbc, 0xe4, 0x1f, 0x1e, 0x2d, 0x7c, 0xb7, 0xf6, 0xa3, 0xec, 0x3b, 0x4e, 0x93, 0x2e, 0xa4, 0xec, 0x6f, 0xe5, 0x95, 0x94, 0xe8, 0x4, 0xfb}} return a, nil } @@ -317,12 +317,12 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1585895900, 0)} + info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x42, 0xb6, 0xe5, 0x48, 0x41, 0xeb, 0xc0, 0x7e, 0x3b, 0xe6, 0x8e, 0x96, 0x33, 0x20, 0x92, 0x24, 0x5a, 0x60, 0xfa, 0xa0, 0x3, 0x5e, 0x76, 0x4b, 0x89, 0xaa, 0x37, 0x66, 0xbc, 0x26, 0x11}} return a, nil } -var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xce\xc1\x0a\xc2\x30\x0c\x00\xd0\xbb\x5f\x91\xff\xf0\xd4\xb9\xde\xea\x0a\x32\xcf\xa1\x76\x99\x16\xd6\x44\x9a\x28\xf8\xf7\x1e\xbd\x14\xd4\x1f\x78\x3c\x17\x66\x7f\x82\xd9\x0d\xc1\x83\x92\x59\xe1\xab\x82\x1b\x47\x38\xc4\x70\x3e\x4e\xd0\xa8\x8a\x11\xde\x1f\x7a\x43\x16\x2b\x6b\xc9\xc9\x8a\xb0\x22\x71\xba\x6c\xb4\xc0\x10\x63\xf0\x6e\xda\xef\xbe\x51\x4a\xbc\x74\xa0\xdf\x81\x4e\x42\xa9\x3d\xa9\xfd\x7f\xe9\x50\x6b\x93\x8a\x59\xd8\x52\x36\x45\xe1\xed\xf5\xe1\xde\x01\x00\x00\xff\xff\x16\x96\xb2\xb5\x26\x01\x00\x00") +var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xce\x51\x0e\x82\x30\x0c\x00\xd0\x7f\x4f\xd1\x7b\xf8\x35\x64\x7c\x55\x48\x70\x7c\x2f\x08\x45\x97\x40\x6b\xd6\x6a\xe2\xed\x3d\x80\x33\x9a\x78\x81\x97\xe7\x30\xf8\x1e\x82\xab\xd0\x83\x92\x59\xe2\x8b\x82\xab\x6b\x38\x74\x38\x1c\x5b\xc8\xb4\x89\x51\xbc\xdd\xf5\x1a\x59\x2c\x2d\x69\x1a\x2d\x09\x6b\x24\x1e\xcf\x2b\xcd\x50\x75\x1d\x7a\xd7\x42\xed\x1b\x37\x60\x80\xc6\xe1\xc9\xef\x77\xdf\x60\x25\x9e\x0b\xec\x1b\x17\xfa\xe1\x07\xad\xf0\x53\xca\x0f\xca\xff\x36\x0b\xf0\x92\x65\x8b\x93\xb0\x8d\x93\x69\x14\x5e\x9f\x9f\xf0\x57\x00\x00\x00\xff\xff\x30\xc0\x56\xbd\x5d\x01\x00\x00") func _0008_add_push_notificationsUpSqlBytes() ([]byte, error) { return bindataRead( @@ -337,8 +337,8 @@ func _0008_add_push_notificationsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 294, mode: os.FileMode(0644), modTime: time.Unix(1594974529, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x81, 0x4a, 0x9, 0x4d, 0xd9, 0xa1, 0x3d, 0xc5, 0xc3, 0xd7, 0x62, 0x8f, 0xc5, 0xfd, 0xb8, 0xa2, 0x71, 0x1d, 0xbc, 0xa6, 0x0, 0x3, 0x83, 0x7a, 0x12, 0x7b, 0x1f, 0xa9, 0x2, 0x29, 0x1a}} + info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 349, mode: os.FileMode(0644), modTime: time.Unix(1595832401, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5a, 0x0, 0xbf, 0xd0, 0xdd, 0xcd, 0x73, 0xe0, 0x7c, 0x56, 0xef, 0xdc, 0x57, 0x61, 0x94, 0x64, 0x70, 0xb9, 0xfa, 0xa1, 0x2a, 0x36, 0xc, 0x2f, 0xf8, 0x95, 0xa, 0x57, 0x3e, 0x7a, 0xd7, 0x12}} return a, nil } diff --git a/appdatabase/migrations/sql/0008_add_push_notifications.up.sql b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql index c4bc5a993..af74b8a37 100644 --- a/appdatabase/migrations/sql/0008_add_push_notifications.up.sql +++ b/appdatabase/migrations/sql/0008_add_push_notifications.up.sql @@ -1,4 +1,4 @@ -ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN; -ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN; -ALTER TABLE settings ADD COLUMN push_notifications_server_enabled BOOLEAN; -ALTER TABLE settings ADD COLUMN push_notifications_from_contacts_only BOOLEAN; +ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN DEFAULT FALSE; +ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN DEFAULT TRUE; +ALTER TABLE settings ADD COLUMN push_notifications_server_enabled BOOLEAN DEFAULT FALSE; +ALTER TABLE settings ADD COLUMN push_notifications_from_contacts_only BOOLEAN DEFAULT FALSE; diff --git a/multiaccounts/accounts/database.go b/multiaccounts/accounts/database.go index 69990e846..740893a6d 100644 --- a/multiaccounts/accounts/database.go +++ b/multiaccounts/accounts/database.go @@ -38,50 +38,55 @@ type Account struct { type Settings struct { // required - Address types.Address `json:"address"` - ChaosMode bool `json:"chaos-mode?,omitempty"` - Currency string `json:"currency,omitempty"` - CurrentNetwork string `json:"networks/current-network"` - CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` - CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` - DappsAddress types.Address `json:"dapps-address"` - EIP1581Address types.Address `json:"eip1581-address"` - Fleet *string `json:"fleet,omitempty"` - HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` - InstallationID string `json:"installation-id"` - KeyUID string `json:"key-uid"` - KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` - KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` - KeycardPairing string `json:"keycard-pairing,omitempty"` - LastUpdated *int64 `json:"last-updated,omitempty"` - LatestDerivedPath uint `json:"latest-derived-path"` - LogLevel *string `json:"log-level,omitempty"` - Mnemonic *string `json:"mnemonic,omitempty"` - Name string `json:"name,omitempty"` - Networks *json.RawMessage `json:"networks/networks"` - NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` - PhotoPath string `json:"photo-path"` - PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` - PreferredName *string `json:"preferred-name,omitempty"` - PreviewPrivacy bool `json:"preview-privacy?"` - PublicKey string `json:"public-key"` - PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled,omitempty"` - PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only,omitempty"` - RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` - RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled,omitempty"` - SigningPhrase string `json:"signing-phrase"` - StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` - StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` - StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` - SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` - SendPushNotifications bool `json:"send-push-notifications,omitempty"` - Appearance uint `json:"appearance"` - Usernames *json.RawMessage `json:"usernames,omitempty"` - WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` - WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` - WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` - WakuEnabled bool `json:"waku-enabled,omitempty"` - WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` + Address types.Address `json:"address"` + ChaosMode bool `json:"chaos-mode?,omitempty"` + Currency string `json:"currency,omitempty"` + CurrentNetwork string `json:"networks/current-network"` + CustomBootnodes *json.RawMessage `json:"custom-bootnodes,omitempty"` + CustomBootnodesEnabled *json.RawMessage `json:"custom-bootnodes-enabled?,omitempty"` + DappsAddress types.Address `json:"dapps-address"` + EIP1581Address types.Address `json:"eip1581-address"` + Fleet *string `json:"fleet,omitempty"` + HideHomeTooltip bool `json:"hide-home-tooltip?,omitempty"` + InstallationID string `json:"installation-id"` + KeyUID string `json:"key-uid"` + KeycardInstanceUID string `json:"keycard-instance-uid,omitempty"` + KeycardPAiredOn int64 `json:"keycard-paired-on,omitempty"` + KeycardPairing string `json:"keycard-pairing,omitempty"` + LastUpdated *int64 `json:"last-updated,omitempty"` + LatestDerivedPath uint `json:"latest-derived-path"` + LogLevel *string `json:"log-level,omitempty"` + Mnemonic *string `json:"mnemonic,omitempty"` + Name string `json:"name,omitempty"` + Networks *json.RawMessage `json:"networks/networks"` + // NotificationsEnabled indicates whether local notifications should be enabled (android only) + NotificationsEnabled bool `json:"notifications-enabled?,omitempty"` + PhotoPath string `json:"photo-path"` + PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"` + PreferredName *string `json:"preferred-name,omitempty"` + PreviewPrivacy bool `json:"preview-privacy?"` + PublicKey string `json:"public-key"` + // PushNotificationsServerEnabled indicates whether we should be running a push notification server + PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"` + // PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts + PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only?,omitempty"` + RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"` + // RemotePushNotificationsEnabled indicates whether we should be using remote notifications (ios only for now) + RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"` + SigningPhrase string `json:"signing-phrase"` + StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"` + StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"` + StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"` + SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"` + // SendPushNotifications indicates whether we should send push notifications for other clients + SendPushNotifications bool `json:"send-push-notifications?,omitempty"` + Appearance uint `json:"appearance"` + Usernames *json.RawMessage `json:"usernames,omitempty"` + WalletRootAddress types.Address `json:"wallet-root-address,omitempty"` + WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"` + WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"` + WakuEnabled bool `json:"waku-enabled,omitempty"` + WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"` } func NewDB(db *sql.DB) *Database { @@ -250,25 +255,25 @@ func (db *Database) SaveSetting(setting string, value interface{}) error { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET remember_syncing_choice = ? WHERE synthetic_id = 'id'") - case "remote-push-notifications-enabled": + case "remote-push-notifications-enabled?": _, ok := value.(bool) if !ok { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET remote_push_notifications_enabled = ? WHERE synthetic_id = 'id'") - case "push-notifications-server-enabled": + case "push-notifications-server-enabled?": _, ok := value.(bool) if !ok { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET push_notifications_server_enabled = ? WHERE synthetic_id = 'id'") - case "push-notifications-from-contacts-only": + case "push-notifications-from-contacts-only?": _, ok := value.(bool) if !ok { return ErrInvalidConfig } update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'") - case "send-push-notifications": + case "send-push-notifications?": _, ok := value.(bool) if !ok { return ErrInvalidConfig @@ -332,7 +337,7 @@ func (db *Database) GetNodeConfig(nodecfg interface{}) error { func (db *Database) GetSettings() (Settings, error) { var s Settings - err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan( + err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, push_notifications_server_enabled, push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan( &s.Address, &s.ChaosMode, &s.Currency, @@ -355,6 +360,10 @@ func (db *Database) GetSettings() (Settings, error) { &s.Name, &s.Networks, &s.NotificationsEnabled, + &s.PushNotificationsServerEnabled, + &s.PushNotificationsFromContactsOnly, + &s.RemotePushNotificationsEnabled, + &s.SendPushNotifications, &s.PhotoPath, &s.PinnedMailserver, &s.PreferredName, diff --git a/protocol/common/crypto.go b/protocol/common/crypto.go index b5741b2e4..0e0a101c8 100644 --- a/protocol/common/crypto.go +++ b/protocol/common/crypto.go @@ -5,9 +5,11 @@ import ( "crypto/cipher" "crypto/ecdsa" "errors" - "github.com/status-im/status-go/eth-node/crypto" - "golang.org/x/crypto/sha3" "io" + + "golang.org/x/crypto/sha3" + + "github.com/status-im/status-go/eth-node/crypto" ) const nonceLength = 12 diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 3416472ad..aa96a2a73 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -43,15 +43,21 @@ type SentMessage struct { } type MessageProcessor struct { - identity *ecdsa.PrivateKey - datasync *datasync.DataSync - protocol *encryption.Protocol - transport transport.Transport - logger *zap.Logger - ephemeralKeys map[string]*ecdsa.PrivateKey - mutex sync.Mutex + identity *ecdsa.PrivateKey + datasync *datasync.DataSync + protocol *encryption.Protocol + transport transport.Transport + logger *zap.Logger - subscriptions []chan<- *SentMessage + // ephemeralKeys is a map that contains the ephemeral keys of the client, used + // to decrypt messages + ephemeralKeys map[string]*ecdsa.PrivateKey + ephemeralKeysMutex sync.Mutex + + // sentMessagesSubscriptions contains all the subscriptions for sent messages + sentMessagesSubscriptions []chan<- *SentMessage + // sentMessagesSubscriptions contains all the subscriptions for scheduled messages + scheduledMessagesSubscriptions []chan<- *RawMessage featureFlags FeatureFlags } @@ -101,7 +107,7 @@ func NewMessageProcessor( } func (p *MessageProcessor) Stop() { - for _, c := range p.subscriptions { + for _, c := range p.sentMessagesSubscriptions { close(c) } p.datasync.Stop() // idempotent op @@ -118,10 +124,22 @@ func (p *MessageProcessor) SendPrivate( zap.String("public-key", types.EncodeHex(crypto.FromECDSAPub(recipient))), zap.String("site", "SendPrivate"), ) + // Currently we don't support sending through datasync and setting custom waku fields, + // as the datasync interface is not rich enough to propagate that information, so we + // would have to add some complexity to handle this. + if rawMessage.ResendAutomatically && (rawMessage.Sender != nil || rawMessage.SkipEncryption) { + return nil, errors.New("setting identity, skip-encryption and datasync not supported") + } + + // Set sender identity if not specified + if rawMessage.Sender == nil { + rawMessage.Sender = p.identity + } + return p.sendPrivate(ctx, recipient, rawMessage) } -// SendGroupRaw takes encoded data, encrypts it and sends through the wire, +// SendGroup takes encoded data, encrypts it and sends through the wire, // always return the messageID func (p *MessageProcessor) SendGroup( ctx context.Context, @@ -132,15 +150,18 @@ func (p *MessageProcessor) SendGroup( "sending a private group message", zap.String("site", "SendGroup"), ) + // Set sender if not specified if rawMessage.Sender == nil { rawMessage.Sender = p.identity } - // Calculate messageID first + + // Calculate messageID first and set on raw message wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) + rawMessage.ID = types.EncodeHex(messageID) // Send to each recipients for _, recipient := range recipients { @@ -160,21 +181,17 @@ func (p *MessageProcessor) sendPrivate( ) ([]byte, error) { p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient)))) - if rawMessage.ResendAutomatically && (rawMessage.Sender != nil || rawMessage.SkipNegotiation) { - return nil, errors.New("setting identity, skip-negotiation and datasync not supported") - } - - // If we use our own key we don't skip negotiation - if rawMessage.Sender == nil { - rawMessage.Sender = p.identity - } - wrappedMessage, err := p.wrapMessageV1(rawMessage) if err != nil { return nil, errors.Wrap(err, "failed to wrap message") } messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) + rawMessage.ID = types.EncodeHex(messageID) + + // Notify before dispatching, otherwise the dispatch subscription might happen + // earlier than the scheduled + p.notifyOnScheduledMessage(rawMessage) if p.featureFlags.Datasync && rawMessage.ResendAutomatically { // No need to call transport tracking. @@ -183,7 +200,8 @@ func (p *MessageProcessor) sendPrivate( return nil, errors.Wrap(err, "failed to send message with datasync") } - } else if rawMessage.SkipNegotiation { + } else if rawMessage.SkipEncryption { + // When SkipEncryption is set we don't pass the message to the encryption layer messageIDs := [][]byte{messageID} hash, newMessage, err := p.sendRawMessage(ctx, recipient, wrappedMessage, messageIDs) if err != nil { @@ -267,7 +285,7 @@ func (p *MessageProcessor) SendPublic( chatName string, rawMessage *RawMessage, ) ([]byte, error) { - var newMessage *types.NewMessage + // Set sender if rawMessage.Sender == nil { rawMessage.Sender = p.identity } @@ -277,20 +295,24 @@ func (p *MessageProcessor) SendPublic( return nil, errors.Wrap(err, "failed to wrap message") } - newMessage = &types.NewMessage{ + newMessage := &types.NewMessage{ TTL: whisperTTL, Payload: wrappedMessage, PowTarget: calculatePoW(wrappedMessage), PowTime: whisperPoWTime, } + messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) + rawMessage.ID = types.EncodeHex(messageID) + + // notify before dispatching + p.notifyOnScheduledMessage(rawMessage) + hash, err := p.transport.SendPublic(ctx, newMessage, chatName) if err != nil { return nil, err } - messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage) - p.transport.Track([][]byte{messageID}, hash, newMessage) return messageID, nil @@ -339,24 +361,32 @@ func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, application return statusMessages, nil } +// fetchDecryptionKey returns the private key associated with this public key, and returns true if it's an ephemeral key +func (p *MessageProcessor) fetchDecryptionKey(destination *ecdsa.PublicKey) (*ecdsa.PrivateKey, bool) { + destinationID := types.EncodeHex(crypto.FromECDSAPub(destination)) + + p.ephemeralKeysMutex.Lock() + decryptionKey, ok := p.ephemeralKeys[destinationID] + p.ephemeralKeysMutex.Unlock() + + // the key is not there, fallback on identity + if !ok { + return p.identity, false + } + return decryptionKey, true +} + func (p *MessageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error { logger := p.logger.With(zap.String("site", "handleEncryptionLayer")) publicKey := message.SigPubKey() - destination := message.Dst - destinationID := types.EncodeHex(crypto.FromECDSAPub(destination)) - p.mutex.Lock() - decryptionKey, ok := p.ephemeralKeys[destinationID] - p.mutex.Unlock() - logger.Info("destination id", zap.String("desti", destinationID)) - skipNegotiation := true - if !ok { - skipNegotiation = false - decryptionKey = p.identity - } + // if it's an ephemeral key, we don't negotiate a topic + decryptionKey, skipNegotiation := p.fetchDecryptionKey(message.Dst) err := message.HandleEncryption(decryptionKey, publicKey, p.protocol, skipNegotiation) - if err == encryption.ErrDeviceNotFound { + + // if it's an ephemeral key, we don't have to handle a device not found error + if err == encryption.ErrDeviceNotFound && !skipNegotiation { if err := p.handleErrDeviceNotFound(ctx, publicKey); err != nil { logger.Error("failed to handle ErrDeviceNotFound", zap.Error(err)) } @@ -464,7 +494,6 @@ func (p *MessageProcessor) sendRawMessage(ctx context.Context, publicKey *ecdsa. } return hash, newMessage, nil - } // sendMessageSpec analyses the spec properties and selects a proper transport method. @@ -496,34 +525,60 @@ func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa MessageIDs: messageIDs, } - logger.Debug("subscriptions", zap.Int("count", len(p.subscriptions))) - // Publish on channels, drop if buffer is full - for _, c := range p.subscriptions { - logger.Debug("sending on subscription") - select { - case c <- sentMessage: - default: - logger.Warn("subscription channel full, dropping message") - } - } + p.notifyOnSentMessage(sentMessage) return hash, newMessage, nil } -func (p *MessageProcessor) Subscribe() <-chan *SentMessage { +// SubscribeToSentMessages returns a channel where we publish every time a message is sent +func (p *MessageProcessor) SubscribeToSentMessages() <-chan *SentMessage { c := make(chan *SentMessage, 100) - p.subscriptions = append(p.subscriptions, c) + p.sentMessagesSubscriptions = append(p.sentMessagesSubscriptions, c) return c } +func (p *MessageProcessor) notifyOnSentMessage(sentMessage *SentMessage) { + // Publish on channels, drop if buffer is full + for _, c := range p.sentMessagesSubscriptions { + select { + case c <- sentMessage: + default: + p.logger.Warn("sent messages subscription channel full, dropping message") + } + } + +} + +// SubscribeToScheduledMessages returns a channel where we publish every time a message is scheduled for sending +func (p *MessageProcessor) SubscribeToScheduledMessages() <-chan *RawMessage { + c := make(chan *RawMessage, 100) + p.scheduledMessagesSubscriptions = append(p.scheduledMessagesSubscriptions, c) + return c +} + +func (p *MessageProcessor) notifyOnScheduledMessage(message *RawMessage) { + // Publish on channels, drop if buffer is full + for _, c := range p.scheduledMessagesSubscriptions { + select { + case c <- message: + default: + p.logger.Warn("scheduled messages subscription channel full, dropping message") + } + } +} + func (p *MessageProcessor) JoinPublic(chatID string) error { return p.transport.JoinPublic(chatID) } -func (p *MessageProcessor) LoadKeyFilters(privateKey *ecdsa.PrivateKey) (*transport.Filter, error) { - p.mutex.Lock() +// AddEphemeralKey adds an ephemeral key that we will be listening to +// note that we never removed them from now, as waku/whisper does not +// recalculate topics on removal, so effectively there's no benefit. +// On restart they will be gone. +func (p *MessageProcessor) AddEphemeralKey(privateKey *ecdsa.PrivateKey) (*transport.Filter, error) { + p.ephemeralKeysMutex.Lock() p.ephemeralKeys[types.EncodeHex(crypto.FromECDSAPub(&privateKey.PublicKey))] = privateKey - p.mutex.Unlock() + p.ephemeralKeysMutex.Unlock() return p.transport.LoadKeyFilters(privateKey) } diff --git a/protocol/common/message_processor_test.go b/protocol/common/message_processor_test.go index 1abdceaa3..9cabbf935 100644 --- a/protocol/common/message_processor_test.go +++ b/protocol/common/message_processor_test.go @@ -1,7 +1,6 @@ package common import ( - "github.com/status-im/status-go/protocol" "io/ioutil" "os" "path/filepath" @@ -34,22 +33,20 @@ func TestMessageProcessorSuite(t *testing.T) { type MessageProcessorSuite struct { suite.Suite - processor *messageProcessor + processor *MessageProcessor tmpDir string - testMessage protocol.Message + testMessage protobuf.ChatMessage logger *zap.Logger } func (s *MessageProcessorSuite) SetupTest() { - s.testMessage = protocol.Message{ - ChatMessage: protobuf.ChatMessage{ - Text: "abc123", - ChatId: "testing-adamb", - ContentType: protobuf.ChatMessage_TEXT_PLAIN, - MessageType: protobuf.ChatMessage_PUBLIC_GROUP, - Clock: 154593077368201, - Timestamp: 1545930773682, - }, + s.testMessage = protobuf.ChatMessage{ + Text: "abc123", + ChatId: "testing-adamb", + ContentType: protobuf.ChatMessage_TEXT_PLAIN, + MessageType: protobuf.ChatMessage_PUBLIC_GROUP, + Clock: 154593077368201, + Timestamp: 1545930773682, } var err error @@ -82,8 +79,6 @@ func (s *MessageProcessorSuite) SetupTest() { whisperConfig.MinimumAcceptedPOW = 0 shh := whisper.New(&whisperConfig) s.Require().NoError(shh.Start(nil)) - config := &protocol.config{} - s.Require().NoError(protocol.WithDatasync()(config)) whisperTransport, err := transport.NewTransport( gethbridge.NewGethWhisperWrapper(shh), @@ -95,14 +90,13 @@ func (s *MessageProcessorSuite) SetupTest() { ) s.Require().NoError(err) - s.processor, err = newMessageProcessor( + s.processor, err = NewMessageProcessor( identity, database, encryptionProtocol, whisperTransport, s.logger, - protocol.featureFlags{}, - nil, + FeatureFlags{}, ) s.Require().NoError(err) } @@ -129,7 +123,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = wrappedPayload - decodedMessages, err := s.processor.handleMessages(message, true) + decodedMessages, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) s.Require().Equal(1, len(decodedMessages)) @@ -137,7 +131,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() { s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID) parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) - s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) + s.Require().True(proto.Equal(&s.testMessage, &parsedMessage)) s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } @@ -165,7 +159,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = marshalledDataSyncMessage - decodedMessages, err := s.processor.handleMessages(message, true) + decodedMessages, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) // We send two messages, the unwrapped one will be attributed to the relayer, while the wrapped one will be attributed to the author @@ -174,7 +168,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID) s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) - s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) + s.Require().True(proto.Equal(&s.testMessage, &parsedMessage)) s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } @@ -232,7 +226,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = encryptedPayload - decodedMessages, err := s.processor.handleMessages(message, true) + decodedMessages, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) // We send two messages, the unwrapped one will be attributed to the relayer, @@ -242,6 +236,6 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID) s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload) parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage) - s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage)) + s.Require().True(proto.Equal(&s.testMessage, &parsedMessage)) s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type) } diff --git a/protocol/common/raw_message.go b/protocol/common/raw_message.go index 573810a2f..9ac87cebe 100644 --- a/protocol/common/raw_message.go +++ b/protocol/common/raw_message.go @@ -9,15 +9,16 @@ import ( // RawMessage represent a sent or received message, kept for being able // to re-send/propagate type RawMessage struct { - ID string - LocalChatID string - LastSent uint64 - SendCount int - Sent bool - ResendAutomatically bool - SkipNegotiation bool - MessageType protobuf.ApplicationMetadataMessage_Type - Payload []byte - Sender *ecdsa.PrivateKey - Recipients []*ecdsa.PublicKey + ID string + LocalChatID string + LastSent uint64 + SendCount int + Sent bool + ResendAutomatically bool + SkipEncryption bool + SendPushNotification bool + MessageType protobuf.ApplicationMetadataMessage_Type + Payload []byte + Sender *ecdsa.PrivateKey + Recipients []*ecdsa.PublicKey } diff --git a/protocol/message_handler.go b/protocol/message_handler.go index b66365403..5713378a8 100644 --- a/protocol/message_handler.go +++ b/protocol/message_handler.go @@ -4,12 +4,12 @@ import ( "crypto/ecdsa" "encoding/hex" "fmt" - "github.com/status-im/status-go/protocol/common" "github.com/pkg/errors" "go.uber.org/zap" "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/protobuf" v1protocol "github.com/status-im/status-go/protocol/v1" diff --git a/protocol/messenger.go b/protocol/messenger.go index 1e15f68aa..1b1bb70d5 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ecdsa" "database/sql" - "github.com/status-im/status-go/protocol/common" "io/ioutil" "math/rand" "os" @@ -20,6 +19,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" enstypes "github.com/status-im/status-go/eth-node/types/ens" + "github.com/status-im/status-go/protocol/common" "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/sharedsecret" @@ -27,8 +27,8 @@ import ( "github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/images" "github.com/status-im/status-go/protocol/protobuf" - "github.com/status-im/status-go/protocol/push_notification_client" - "github.com/status-im/status-go/protocol/push_notification_server" + "github.com/status-im/status-go/protocol/pushnotificationclient" + "github.com/status-im/status-go/protocol/pushnotificationserver" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/transport" wakutransp "github.com/status-im/status-go/protocol/transport/waku" @@ -61,8 +61,8 @@ type Messenger struct { encryptor *encryption.Protocol processor *common.MessageProcessor handler *MessageHandler - pushNotificationClient *push_notification_client.Client - pushNotificationServer *push_notification_server.Server + pushNotificationClient *pushnotificationclient.Client + pushNotificationServer *pushnotificationserver.Server logger *zap.Logger verifyTransactionClient EthClient featureFlags common.FeatureFlags @@ -248,27 +248,29 @@ func NewMessenger( return nil, errors.Wrap(err, "failed to create messageProcessor") } - var pushNotificationServer *push_notification_server.Server + // Initialize push notification server + var pushNotificationServer *pushnotificationserver.Server if c.pushNotificationServerConfig != nil { c.pushNotificationServerConfig.Identity = identity - pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(database) - pushNotificationServer = push_notification_server.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor) + pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(database) + pushNotificationServer = pushnotificationserver.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor) } - pushNotificationClientPersistence := push_notification_client.NewPersistence(database) + // Initialize push notification client + pushNotificationClientPersistence := pushnotificationclient.NewPersistence(database) pushNotificationClientConfig := c.pushNotificationClientConfig if pushNotificationClientConfig == nil { - pushNotificationClientConfig = &push_notification_client.Config{} + pushNotificationClientConfig = &pushnotificationclient.Config{} } - // Overriding until we handle sending/receiving from multiple identities + // Overriding until we handle different identities pushNotificationClientConfig.Identity = identity // Hardcoding this for now, as it's the only one we support pushNotificationClientConfig.TokenType = protobuf.PushNotificationRegistration_APN_TOKEN pushNotificationClientConfig.Logger = logger pushNotificationClientConfig.InstallationID = installationID - pushNotificationClient := push_notification_client.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor) + pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor) handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database}) @@ -318,6 +320,8 @@ func (m *Messenger) Start() error { return err } } + + // Start push notification client if m.pushNotificationClient != nil { if err := m.pushNotificationClient.Start(); err != nil { return err @@ -1145,7 +1149,7 @@ func (m *Messenger) saveContact(contact *Contact) error { } } - // We check if it should re-register + // We check if it should re-register with the push notification server shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact)) err = m.persistence.SaveContact(contact, nil) @@ -1379,7 +1383,6 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes } logger := m.logger.With(zap.String("site", "Send"), zap.String("chatID", message.ChatId)) - logger.Info("SENDING CHAT MESSAGE") var response MessengerResponse // A valid added chat is required. @@ -1427,25 +1430,17 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes return nil, errors.New("chat type not supported") } - // THERE'S A RACE CONDITION, WE SHOULD CALCULATE AND TRACK THE ID FIRST id, err := m.dispatchMessage(ctx, &common.RawMessage{ - LocalChatID: chat.ID, - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, - ResendAutomatically: true, + LocalChatID: chat.ID, + SendPushNotification: !chat.Public(), + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, + ResendAutomatically: true, }) if err != nil { return nil, err } - // If the chat is not public, we instruct the pushNotificationService to send a notification - if !chat.Public() && m.pushNotificationClient != nil { - if err := m.pushNotificationClient.NotifyOnMessageID(chat.ID, id); err != nil { - return nil, err - } - - } - message.ID = types.EncodeHex(id) err = message.PrepareContent() if err != nil { @@ -1982,8 +1977,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationQuery") - // TODO: Compare DST with Identity - if err := m.pushNotificationServer.HandlePushNotificationQuery2(publicKey, msg.ID, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil { + if err := m.pushNotificationServer.HandlePushNotificationQuery(publicKey, msg.ID, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil { logger.Warn("failed to handle PushNotificationQuery", zap.Error(err)) } // We continue in any case, no changes to messenger @@ -1994,7 +1988,6 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationRegistrationResponse") - // TODO: Compare DST with Identity if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil { logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err)) } @@ -2006,7 +1999,6 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationResponse") - // TODO: Compare DST with Identity if err := m.pushNotificationClient.HandlePushNotificationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationResponse)); err != nil { logger.Warn("failed to handle PushNotificationResponse", zap.Error(err)) } @@ -2019,7 +2011,6 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationQueryResponse") - // TODO: Compare DST with Identity if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil { logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err)) } @@ -2032,8 +2023,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationRequest") - // TODO: Compare DST with Identity - if err := m.pushNotificationServer.HandlePushNotificationRequest2(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRequest)); err != nil { + if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRequest)); err != nil { logger.Warn("failed to handle PushNotificationRequest", zap.Error(err)) } // We continue in any case, no changes to messenger @@ -2047,8 +2037,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationRegistration") - // TODO: Compare DST with Identity - if err := m.pushNotificationServer.HandlePushNotificationRegistration2(publicKey, msg.ParsedMessage.([]byte)); err != nil { + if err := m.pushNotificationServer.HandlePushNotificationRegistration(publicKey, msg.ParsedMessage.([]byte)); err != nil { logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err)) } // We continue in any case, no changes to messenger @@ -2134,6 +2123,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte return messageState.Response, nil } +// SetMailserver sets the currently used mailserver func (m *Messenger) SetMailserver(peer []byte) { m.mailserver = peer } @@ -3069,12 +3059,12 @@ func (m *Messenger) Timesource() TimeSource { return m.getTimesource() } -// AddPushNotificationServer adds a push notification server -func (m *Messenger) AddPushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error { +// AddPushNotificationsServer adds a push notification server +func (m *Messenger) AddPushNotificationsServer(ctx context.Context, publicKey *ecdsa.PublicKey) error { if m.pushNotificationClient == nil { return errors.New("push notification client not enabled") } - return m.pushNotificationClient.AddPushNotificationServer(publicKey) + return m.pushNotificationClient.AddPushNotificationsServer(publicKey) } // RemovePushNotificationServer removes a push notification server @@ -3085,10 +3075,12 @@ func (m *Messenger) RemovePushNotificationServer(ctx context.Context, publicKey return m.pushNotificationClient.RemovePushNotificationServer(publicKey) } +// UnregisterFromPushNotifications unregister from any server func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error { return m.pushNotificationClient.Unregister() } +// DisableSendingPushNotifications signals the client not to send any push notification func (m *Messenger) DisableSendingPushNotifications() error { if m.pushNotificationClient == nil { return errors.New("push notification client not enabled") @@ -3097,6 +3089,7 @@ func (m *Messenger) DisableSendingPushNotifications() error { return nil } +// EnableSendingPushNotifications signals the client to send push notifications func (m *Messenger) EnableSendingPushNotifications() error { if m.pushNotificationClient == nil { return errors.New("push notification client not enabled") @@ -3142,6 +3135,7 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs) } +// RegisteredForPushNotifications returns whether we successfully registered with all the servers func (m *Messenger) RegisteredForPushNotifications() (bool, error) { if m.pushNotificationClient == nil { return false, errors.New("no push notification client") @@ -3149,6 +3143,7 @@ func (m *Messenger) RegisteredForPushNotifications() (bool, error) { return m.pushNotificationClient.Registered() } +// EnablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications only from contacts func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { if m.pushNotificationClient == nil { return errors.New("no push notification client") @@ -3160,6 +3155,7 @@ func (m *Messenger) EnablePushNotificationsFromContactsOnly() error { return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) } +// DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { if m.pushNotificationClient == nil { return errors.New("no push notification client") @@ -3171,27 +3167,30 @@ func (m *Messenger) DisablePushNotificationsFromContactsOnly() error { return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs) } -func (m *Messenger) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { +// GetPushNotificationServers returns the servers used for push notifications +func (m *Messenger) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) { if m.pushNotificationClient == nil { return nil, errors.New("no push notification client") } return m.pushNotificationClient.GetServers() } -func (m *Messenger) StartPushNotificationServer() error { +// StartPushNotificationsServer initialize and start a push notification server, using the current messenger identity key +func (m *Messenger) StartPushNotificationsServer() error { if m.pushNotificationServer == nil { - pushNotificationServerPersistence := push_notification_server.NewSQLitePersistence(m.database) - config := &push_notification_server.Config{ + pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(m.database) + config := &pushnotificationserver.Config{ Logger: m.logger, Identity: m.identity, } - m.pushNotificationServer = push_notification_server.New(config, pushNotificationServerPersistence, m.processor) + m.pushNotificationServer = pushnotificationserver.New(config, pushNotificationServerPersistence, m.processor) } return m.pushNotificationServer.Start() } -func (m *Messenger) StopPushNotificationServer() error { +// StopPushNotificationServer stops the push notification server if running +func (m *Messenger) StopPushNotificationsServer() error { m.pushNotificationServer = nil return nil } diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index 72573dec9..f404a11ae 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -2,13 +2,15 @@ package protocol import ( "database/sql" + + "go.uber.org/zap" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/protobuf" - "github.com/status-im/status-go/protocol/push_notification_client" - "github.com/status-im/status-go/protocol/push_notification_server" + "github.com/status-im/status-go/protocol/pushnotificationclient" + "github.com/status-im/status-go/protocol/pushnotificationserver" "github.com/status-im/status-go/protocol/transport" - "go.uber.org/zap" ) type config struct { @@ -34,8 +36,8 @@ type config struct { verifyTransactionClient EthClient - pushNotificationServerConfig *push_notification_server.Config - pushNotificationClientConfig *push_notification_client.Config + pushNotificationServerConfig *pushnotificationserver.Config + pushNotificationClientConfig *pushnotificationclient.Config logger *zap.Logger } @@ -93,14 +95,14 @@ func WithDatabase(db *sql.DB) Option { } } -func WithPushNotificationServerConfig(pushNotificationServerConfig *push_notification_server.Config) Option { +func WithPushNotificationServerConfig(pushNotificationServerConfig *pushnotificationserver.Config) Option { return func(c *config) error { c.pushNotificationServerConfig = pushNotificationServerConfig return nil } } -func WithPushNotificationClientConfig(pushNotificationClientConfig *push_notification_client.Config) Option { +func WithPushNotificationClientConfig(pushNotificationClientConfig *pushnotificationclient.Config) Option { return func(c *config) error { c.pushNotificationClientConfig = pushNotificationClientConfig return nil diff --git a/protocol/messenger_installations_test.go b/protocol/messenger_installations_test.go index 45a1a4258..8c572e058 100644 --- a/protocol/messenger_installations_test.go +++ b/protocol/messenger_installations_test.go @@ -20,6 +20,8 @@ import ( "github.com/status-im/status-go/waku" ) +const statusChatID = "status" + func TestMessengerInstallationSuite(t *testing.T) { suite.Run(t, new(MessengerInstallationSuite)) } @@ -136,7 +138,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() { s.Require().Equal(contact.ID, actualContact.ID) s.Require().True(actualContact.IsAdded()) - chat := CreatePublicChat("status", s.m.transport) + chat := CreatePublicChat(statusChatID, s.m.transport) err = s.m.SaveChat(&chat) s.Require().NoError(err) @@ -149,7 +151,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() { s.Require().NoError(err) actualChat := response.Chats[0] - s.Require().Equal("status", actualChat.ID) + s.Require().Equal(statusChatID, actualChat.ID) s.Require().True(actualChat.Active) } @@ -166,7 +168,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() { s.Require().NoError(err) // add chat - chat := CreatePublicChat("status", s.m.transport) + chat := CreatePublicChat(statusChatID, s.m.transport) err = s.m.SaveChat(&chat) s.Require().NoError(err) @@ -230,7 +232,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() { var statusChat *Chat for _, c := range allChats { - if c.ID == "status" { + if c.ID == statusChatID { statusChat = c } } diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index eeedb604e..0831a664d 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -12,8 +12,8 @@ // 1589365189_add_pow_target.up.sql (66B) // 1591277220_add_index_messages.down.sql (237B) // 1591277220_add_index_messages.up.sql (240B) -// 1593087212_add_mute_chat.down.sql (0) -// 1593087212_add_mute_chat.up.sql (58B) +// 1593087212_add_mute_chat_and_raw_message_fields.down.sql (0) +// 1593087212_add_mute_chat_and_raw_message_fields.up.sql (215B) // doc.go (850B) package migrations @@ -138,7 +138,7 @@ func _000002_add_last_ens_clock_valueDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -158,7 +158,7 @@ func _000002_add_last_ens_clock_valueUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4d, 0x3, 0x8f, 0xd5, 0x85, 0x83, 0x47, 0xbe, 0xf9, 0x82, 0x7e, 0x81, 0xa4, 0xbd, 0xaa, 0xd5, 0x98, 0x18, 0x5, 0x2d, 0x82, 0x42, 0x3b, 0x3, 0x50, 0xc3, 0x1e, 0x84, 0x35, 0xf, 0xb6, 0x2b}} return a, nil } @@ -178,7 +178,7 @@ func _1586358095_add_replaceDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)} + info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -198,7 +198,7 @@ func _1586358095_add_replaceUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)} + info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xb3, 0xa9, 0xc7, 0x7f, 0x9d, 0x8f, 0x43, 0x8c, 0x9e, 0x58, 0x8d, 0x44, 0xbc, 0xfa, 0x6b, 0x5f, 0x3f, 0x5a, 0xbe, 0xe8, 0xb1, 0x16, 0xf, 0x91, 0x2a, 0xa0, 0x71, 0xbb, 0x8d, 0x6b, 0xcb}} return a, nil } @@ -218,7 +218,7 @@ func _1588665364_add_image_dataDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -238,7 +238,7 @@ func _1588665364_add_image_dataUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}} return a, nil } @@ -258,7 +258,7 @@ func _1589365189_add_pow_targetDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } @@ -278,7 +278,7 @@ func _1589365189_add_pow_targetUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}} return a, nil } @@ -298,7 +298,7 @@ func _1591277220_add_index_messagesDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x79, 0xe5, 0x42, 0x56, 0x64, 0x1d, 0xb7, 0x8a, 0x1b, 0x0, 0x99, 0xf0, 0x18, 0x8c, 0x69, 0xe3, 0x14, 0x3a, 0x7f, 0x78, 0xfe, 0xe3, 0x2e, 0xcb, 0x6e, 0x5c, 0x8c, 0x1f, 0x7b, 0xfc, 0x21, 0xc7}} return a, nil } @@ -318,48 +318,48 @@ func _1591277220_add_index_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1591690523, 0)} + info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}} return a, nil } -var __1593087212_add_mute_chatDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00") +var __1593087212_add_mute_chat_and_raw_message_fieldsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00") -func _1593087212_add_mute_chatDownSqlBytes() ([]byte, error) { +func _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes() ([]byte, error) { return bindataRead( - __1593087212_add_mute_chatDownSql, - "1593087212_add_mute_chat.down.sql", + __1593087212_add_mute_chat_and_raw_message_fieldsDownSql, + "1593087212_add_mute_chat_and_raw_message_fields.down.sql", ) } -func _1593087212_add_mute_chatDownSql() (*asset, error) { - bytes, err := _1593087212_add_mute_chatDownSqlBytes() +func _1593087212_add_mute_chat_and_raw_message_fieldsDownSql() (*asset, error) { + bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1594896559, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}} return a, nil } -var __1593087212_add_mute_chatUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xce\x48\x2c\x29\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\xc8\x2d\x2d\x49\x4d\x51\x70\xf2\xf7\xf7\x71\x75\xf4\x53\x70\x71\x75\x73\x0c\xf5\x09\x51\x70\x73\xf4\x09\x76\xb5\xe6\x02\x04\x00\x00\xff\xff\x59\x4c\x4b\xec\x3a\x00\x00\x00") +var __1593087212_add_mute_chat_and_raw_message_fieldsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xcc\x31\x0e\xc2\x30\x0c\x00\xc0\x9d\x57\xf8\x1f\x4c\x2e\x71\x27\xd3\x48\x90\xce\x51\x94\x1a\x1a\xa1\xa6\x51\xed\x0a\xf1\x7b\x56\x58\x58\x78\xc0\x1d\x72\xa0\x0b\x04\xec\x98\x20\xcf\xc9\x14\xd0\x39\x38\x79\x1e\xcf\x03\x2c\xbb\xc9\x04\x9d\xf7\x4c\x38\x80\xa3\x1e\x47\x0e\xd0\x23\x5f\xe9\x78\xf8\x94\x5b\x7a\xc6\x45\x54\xd3\x5d\xbe\x02\x7d\x94\x16\xa5\xe6\xed\xd5\xac\xac\xf5\xaf\x4a\xea\x14\xdb\xae\x73\xac\xab\x95\x5b\xc9\xe9\xd7\xf8\x0e\x00\x00\xff\xff\xd9\x47\x38\x58\xd7\x00\x00\x00") -func _1593087212_add_mute_chatUpSqlBytes() ([]byte, error) { +func _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes() ([]byte, error) { return bindataRead( - __1593087212_add_mute_chatUpSql, - "1593087212_add_mute_chat.up.sql", + __1593087212_add_mute_chat_and_raw_message_fieldsUpSql, + "1593087212_add_mute_chat_and_raw_message_fields.up.sql", ) } -func _1593087212_add_mute_chatUpSql() (*asset, error) { - bytes, err := _1593087212_add_mute_chatUpSqlBytes() +func _1593087212_add_mute_chat_and_raw_message_fieldsUpSql() (*asset, error) { + bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes() if err != nil { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat.up.sql", size: 58, mode: os.FileMode(0644), modTime: time.Unix(1594896559, 0)} - a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe, 0x9, 0xa0, 0x4a, 0x8e, 0x23, 0xe4, 0xce, 0xbc, 0xd4, 0x9, 0xeb, 0xf9, 0x67, 0x90, 0xc0, 0x4b, 0x67, 0x84, 0xe4, 0x42, 0x8d, 0x0, 0x17, 0x29, 0x7f, 0x12, 0xbf, 0x7d, 0x4e, 0x78, 0xec}} + info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x73, 0x99, 0x61, 0xd1, 0xaa, 0xb4, 0xbf, 0xaf, 0xd7, 0x20, 0x17, 0x40, 0xf9, 0x2, 0xfb, 0xcc, 0x40, 0x2a, 0xd, 0x86, 0x36, 0x30, 0x88, 0x89, 0x25, 0x80, 0x42, 0xb0, 0x5b, 0xe9, 0x73, 0x78}} return a, nil } @@ -378,7 +378,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)} + info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa0, 0xcc, 0x41, 0xe1, 0x61, 0x12, 0x97, 0xe, 0x36, 0x8c, 0xa7, 0x9e, 0xe0, 0x6e, 0x59, 0x9e, 0xee, 0xd5, 0x4a, 0xcf, 0x1e, 0x60, 0xd6, 0xc3, 0x3a, 0xc9, 0x6c, 0xf2, 0x86, 0x5a, 0xb4, 0x1e}} return a, nil } @@ -498,9 +498,9 @@ var _bindata = map[string]func() (*asset, error){ "1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql, - "1593087212_add_mute_chat.down.sql": _1593087212_add_mute_chatDownSql, + "1593087212_add_mute_chat_and_raw_message_fields.down.sql": _1593087212_add_mute_chat_and_raw_message_fieldsDownSql, - "1593087212_add_mute_chat.up.sql": _1593087212_add_mute_chatUpSql, + "1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql, "doc.go": docGo, } @@ -546,21 +546,21 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}}, - "000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}}, - "000002_add_last_ens_clock_value.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}}, - "000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}}, - "1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}}, - "1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}}, - "1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}}, - "1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}}, - "1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}}, - "1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}}, - "1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}}, - "1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}}, - "1593087212_add_mute_chat.down.sql": &bintree{_1593087212_add_mute_chatDownSql, map[string]*bintree{}}, - "1593087212_add_mute_chat.up.sql": &bintree{_1593087212_add_mute_chatUpSql, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, + "000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}}, + "000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}}, + "000002_add_last_ens_clock_value.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}}, + "000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}}, + "1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}}, + "1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}}, + "1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}}, + "1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}}, + "1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}}, + "1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}}, + "1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}}, + "1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}}, + "1593087212_add_mute_chat_and_raw_message_fields.down.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsDownSql, map[string]*bintree{}}, + "1593087212_add_mute_chat_and_raw_message_fields.up.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql b/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql deleted file mode 100644 index b0c2d5215..000000000 --- a/protocol/migrations/sqlite/1593087212_add_mute_chat.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE chats ADD COLUMN muted BOOLEAN DEFAULT FALSE; diff --git a/protocol/migrations/sqlite/1593087212_add_mute_chat.down.sql b/protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.down.sql similarity index 100% rename from protocol/migrations/sqlite/1593087212_add_mute_chat.down.sql rename to protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.down.sql diff --git a/protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.up.sql b/protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.up.sql new file mode 100644 index 000000000..20fc7405d --- /dev/null +++ b/protocol/migrations/sqlite/1593087212_add_mute_chat_and_raw_message_fields.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE chats ADD COLUMN muted BOOLEAN DEFAULT FALSE; +ALTER TABLE raw_messages ADD COLUMN skip_encryption BOOLEAN DEFAULT FALSE; +ALTER TABLE raw_messages ADD COLUMN send_push_notification BOOLEAN DEFAULT FALSE; diff --git a/protocol/persistence.go b/protocol/persistence.go index 916fcdd94..7ed18fd96 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -7,9 +7,9 @@ import ( "encoding/gob" "github.com/pkg/errors" - "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/protocol/common" ) var ( @@ -403,9 +403,11 @@ func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error { message_type, resend_automatically, recipients, + skip_encryption, + send_push_notification, payload ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, message.ID, message.LocalChatID, message.LastSent, @@ -414,6 +416,8 @@ func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error { message.MessageType, message.ResendAutomatically, encodedRecipients.Bytes(), + message.SkipEncryption, + message.SendPushNotification, message.Payload) return err } @@ -433,6 +437,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error message_type, resend_automatically, recipients, + skip_encryption, + send_push_notification, payload FROM raw_messages @@ -448,6 +454,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error &message.MessageType, &message.ResendAutomatically, &encodedRecipients, + &message.SkipEncryption, + &message.SendPushNotification, &message.Payload, ) if err != nil { diff --git a/protocol/protobuf/push_notifications.pb.go b/protocol/protobuf/push_notifications.pb.go index 9e062eef4..02dd42b56 100644 --- a/protocol/protobuf/push_notifications.pb.go +++ b/protocol/protobuf/push_notifications.pb.go @@ -110,17 +110,17 @@ func (x PushNotificationReport_ErrorType) String() string { } func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{9, 0} + return fileDescriptor_200acd86044eaa5d, []int{8, 0} } type PushNotificationRegistration struct { TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"` - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` + DeviceToken string `protobuf:"bytes,2,opt,name=device_token,json=deviceToken,proto3" json:"device_token,omitempty"` InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"` Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` - AllowedUserList [][]byte `protobuf:"bytes,7,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + AllowedKeyList [][]byte `protobuf:"bytes,7,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"` BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"` Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"` Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"` @@ -162,9 +162,9 @@ func (m *PushNotificationRegistration) GetTokenType() PushNotificationRegistrati return PushNotificationRegistration_UNKNOWN_TOKEN_TYPE } -func (m *PushNotificationRegistration) GetToken() string { +func (m *PushNotificationRegistration) GetDeviceToken() string { if m != nil { - return m.Token + return m.DeviceToken } return "" } @@ -197,9 +197,9 @@ func (m *PushNotificationRegistration) GetVersion() uint64 { return 0 } -func (m *PushNotificationRegistration) GetAllowedUserList() [][]byte { +func (m *PushNotificationRegistration) GetAllowedKeyList() [][]byte { if m != nil { - return m.AllowedUserList + return m.AllowedKeyList } return nil } @@ -287,73 +287,18 @@ func (m *PushNotificationRegistrationResponse) GetRequestId() []byte { return nil } -type PushNotificationAdvertisementInfo struct { - PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` - InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushNotificationAdvertisementInfo) Reset() { *m = PushNotificationAdvertisementInfo{} } -func (m *PushNotificationAdvertisementInfo) String() string { return proto.CompactTextString(m) } -func (*PushNotificationAdvertisementInfo) ProtoMessage() {} -func (*PushNotificationAdvertisementInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{2} -} - -func (m *PushNotificationAdvertisementInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushNotificationAdvertisementInfo.Unmarshal(m, b) -} -func (m *PushNotificationAdvertisementInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushNotificationAdvertisementInfo.Marshal(b, m, deterministic) -} -func (m *PushNotificationAdvertisementInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushNotificationAdvertisementInfo.Merge(m, src) -} -func (m *PushNotificationAdvertisementInfo) XXX_Size() int { - return xxx_messageInfo_PushNotificationAdvertisementInfo.Size(m) -} -func (m *PushNotificationAdvertisementInfo) XXX_DiscardUnknown() { - xxx_messageInfo_PushNotificationAdvertisementInfo.DiscardUnknown(m) -} - -var xxx_messageInfo_PushNotificationAdvertisementInfo proto.InternalMessageInfo - -func (m *PushNotificationAdvertisementInfo) GetPublicKey() []byte { - if m != nil { - return m.PublicKey - } - return nil -} - -func (m *PushNotificationAdvertisementInfo) GetAccessToken() string { - if m != nil { - return m.AccessToken - } - return "" -} - -func (m *PushNotificationAdvertisementInfo) GetInstallationId() string { - if m != nil { - return m.InstallationId - } - return "" -} - type ContactCodeAdvertisement struct { - PushNotificationInfo []*PushNotificationAdvertisementInfo `protobuf:"bytes,1,rep,name=push_notification_info,json=pushNotificationInfo,proto3" json:"push_notification_info,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PushNotificationInfo []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=push_notification_info,json=pushNotificationInfo,proto3" json:"push_notification_info,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertisement{} } func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) } func (*ContactCodeAdvertisement) ProtoMessage() {} func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{3} + return fileDescriptor_200acd86044eaa5d, []int{2} } func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error { @@ -374,7 +319,7 @@ func (m *ContactCodeAdvertisement) XXX_DiscardUnknown() { var xxx_messageInfo_ContactCodeAdvertisement proto.InternalMessageInfo -func (m *ContactCodeAdvertisement) GetPushNotificationInfo() []*PushNotificationAdvertisementInfo { +func (m *ContactCodeAdvertisement) GetPushNotificationInfo() []*PushNotificationQueryInfo { if m != nil { return m.PushNotificationInfo } @@ -392,7 +337,7 @@ func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} } func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) } func (*PushNotificationQuery) ProtoMessage() {} func (*PushNotificationQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{4} + return fileDescriptor_200acd86044eaa5d, []int{3} } func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error { @@ -424,9 +369,10 @@ type PushNotificationQueryInfo struct { AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"` PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - AllowedUserList [][]byte `protobuf:"bytes,4,rep,name=allowed_user_list,json=allowedUserList,proto3" json:"allowed_user_list,omitempty"` + AllowedKeyList [][]byte `protobuf:"bytes,4,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"` Grant []byte `protobuf:"bytes,5,opt,name=grant,proto3" json:"grant,omitempty"` Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"` + ServerPublicKey []byte `protobuf:"bytes,7,opt,name=server_public_key,json=serverPublicKey,proto3" json:"server_public_key,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -436,7 +382,7 @@ func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQuery func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryInfo) ProtoMessage() {} func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{5} + return fileDescriptor_200acd86044eaa5d, []int{4} } func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error { @@ -478,9 +424,9 @@ func (m *PushNotificationQueryInfo) GetPublicKey() []byte { return nil } -func (m *PushNotificationQueryInfo) GetAllowedUserList() [][]byte { +func (m *PushNotificationQueryInfo) GetAllowedKeyList() [][]byte { if m != nil { - return m.AllowedUserList + return m.AllowedKeyList } return nil } @@ -499,6 +445,13 @@ func (m *PushNotificationQueryInfo) GetVersion() uint64 { return 0 } +func (m *PushNotificationQueryInfo) GetServerPublicKey() []byte { + if m != nil { + return m.ServerPublicKey + } + return nil +} + type PushNotificationQueryResponse struct { Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"` @@ -512,7 +465,7 @@ func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQ func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationQueryResponse) ProtoMessage() {} func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{6} + return fileDescriptor_200acd86044eaa5d, []int{5} } func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error { @@ -569,7 +522,7 @@ func (m *PushNotification) Reset() { *m = PushNotification{} } func (m *PushNotification) String() string { return proto.CompactTextString(m) } func (*PushNotification) ProtoMessage() {} func (*PushNotification) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{7} + return fileDescriptor_200acd86044eaa5d, []int{6} } func (m *PushNotification) XXX_Unmarshal(b []byte) error { @@ -637,7 +590,7 @@ func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) } func (*PushNotificationRequest) ProtoMessage() {} func (*PushNotificationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{8} + return fileDescriptor_200acd86044eaa5d, []int{7} } func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error { @@ -686,7 +639,7 @@ func (m *PushNotificationReport) Reset() { *m = PushNotificationReport{} func (m *PushNotificationReport) String() string { return proto.CompactTextString(m) } func (*PushNotificationReport) ProtoMessage() {} func (*PushNotificationReport) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{9} + return fileDescriptor_200acd86044eaa5d, []int{8} } func (m *PushNotificationReport) XXX_Unmarshal(b []byte) error { @@ -747,7 +700,7 @@ func (m *PushNotificationResponse) Reset() { *m = PushNotificationRespon func (m *PushNotificationResponse) String() string { return proto.CompactTextString(m) } func (*PushNotificationResponse) ProtoMessage() {} func (*PushNotificationResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_200acd86044eaa5d, []int{10} + return fileDescriptor_200acd86044eaa5d, []int{9} } func (m *PushNotificationResponse) XXX_Unmarshal(b []byte) error { @@ -788,7 +741,6 @@ func init() { proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value) proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration") proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse") - proto.RegisterType((*PushNotificationAdvertisementInfo)(nil), "protobuf.PushNotificationAdvertisementInfo") proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement") proto.RegisterType((*PushNotificationQuery)(nil), "protobuf.PushNotificationQuery") proto.RegisterType((*PushNotificationQueryInfo)(nil), "protobuf.PushNotificationQueryInfo") @@ -802,61 +754,60 @@ func init() { func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) } var fileDescriptor_200acd86044eaa5d = []byte{ - // 891 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xc7, 0xb1, 0x93, 0xb6, 0xf1, 0x49, 0x68, 0xb3, 0xa3, 0xb6, 0x6b, 0x56, 0x2c, 0x64, 0x0d, - 0x12, 0xd1, 0x22, 0x45, 0xa8, 0x48, 0xec, 0x8a, 0x2b, 0x42, 0xeb, 0x16, 0xab, 0x8d, 0x1d, 0x26, - 0x2e, 0x2b, 0x24, 0x24, 0xcb, 0x89, 0x27, 0xad, 0x55, 0xd7, 0x63, 0x66, 0xc6, 0x8b, 0x72, 0x81, - 0xc4, 0x13, 0x20, 0x21, 0x71, 0xc5, 0x53, 0x20, 0x1e, 0x89, 0x27, 0x41, 0x1e, 0xdb, 0xc1, 0xf9, - 0x68, 0x9a, 0x8b, 0xbd, 0x8a, 0xcf, 0x99, 0x73, 0x66, 0xe6, 0xfc, 0xce, 0x99, 0x7f, 0x40, 0x4f, - 0x52, 0x7e, 0xeb, 0xc5, 0x54, 0x84, 0xd3, 0x70, 0xe2, 0x8b, 0x90, 0xc6, 0xbc, 0x97, 0x30, 0x2a, - 0x28, 0x6a, 0xc8, 0x9f, 0x71, 0x3a, 0x35, 0xfe, 0xac, 0xc3, 0x87, 0xc3, 0x94, 0xdf, 0xda, 0x95, - 0x28, 0x4c, 0x6e, 0x42, 0x2e, 0x98, 0xfc, 0x46, 0x0e, 0x80, 0xa0, 0x77, 0x24, 0xf6, 0xc4, 0x2c, - 0x21, 0xba, 0xd2, 0x51, 0xba, 0xfb, 0x27, 0x5f, 0xf4, 0xca, 0xfc, 0xde, 0xa6, 0xdc, 0x9e, 0x9b, - 0x25, 0xba, 0xb3, 0x84, 0x60, 0x4d, 0x94, 0x9f, 0xe8, 0x10, 0x76, 0xa4, 0xa1, 0xab, 0x1d, 0xa5, - 0xab, 0xe1, 0xdc, 0x40, 0x9f, 0xc1, 0x41, 0x18, 0x73, 0xe1, 0x47, 0x91, 0x4c, 0xf5, 0xc2, 0x40, - 0xaf, 0xc9, 0xf5, 0xfd, 0xaa, 0xdb, 0x0a, 0xd0, 0x0b, 0x68, 0xf9, 0x93, 0x09, 0xe1, 0xdc, 0xcb, - 0x77, 0xa9, 0xcb, 0xa8, 0x66, 0xee, 0x93, 0x07, 0x22, 0x1d, 0xf6, 0x48, 0xec, 0x8f, 0x23, 0x12, - 0xe8, 0x3b, 0x1d, 0xa5, 0xdb, 0xc0, 0xa5, 0x99, 0xad, 0xbc, 0x25, 0x8c, 0x87, 0x34, 0xd6, 0x77, - 0x3b, 0x4a, 0xb7, 0x8e, 0x4b, 0x13, 0xbd, 0x84, 0x27, 0x7e, 0x14, 0xd1, 0x5f, 0x48, 0xe0, 0xa5, - 0x9c, 0x30, 0x2f, 0x0a, 0xb9, 0xd0, 0xf7, 0x3a, 0xb5, 0x6e, 0x0b, 0x1f, 0x14, 0x0b, 0xd7, 0x9c, - 0xb0, 0xab, 0x90, 0x8b, 0x2c, 0x76, 0x1c, 0xd1, 0xc9, 0x1d, 0x09, 0xbc, 0xc9, 0xad, 0x2f, 0xf2, - 0xd8, 0x46, 0x1e, 0x5b, 0x2c, 0x9c, 0xde, 0xfa, 0x42, 0xc6, 0x7e, 0x04, 0x90, 0xc6, 0x4c, 0x42, - 0x21, 0x4c, 0xd7, 0xe4, 0x75, 0x2a, 0x9e, 0x8c, 0xc6, 0x0d, 0xf3, 0x63, 0xa1, 0x43, 0x47, 0xe9, - 0xb6, 0x70, 0x6e, 0xa0, 0x57, 0xa0, 0xcb, 0x43, 0xbd, 0x29, 0xa3, 0xf7, 0xde, 0x84, 0xc6, 0xc2, - 0x9f, 0x08, 0xee, 0xd1, 0x38, 0x9a, 0xe9, 0x4d, 0xb9, 0xc7, 0x91, 0x5c, 0x3f, 0x67, 0xf4, 0xfe, - 0xb4, 0x58, 0x75, 0xe2, 0x68, 0x66, 0x9c, 0x83, 0x36, 0x87, 0x8e, 0x8e, 0x01, 0x5d, 0xdb, 0x97, - 0xb6, 0xf3, 0xc6, 0xf6, 0x5c, 0xe7, 0xd2, 0xb4, 0x3d, 0xf7, 0xc7, 0xa1, 0xd9, 0x7e, 0x0f, 0xbd, - 0x0f, 0x5a, 0x7f, 0x58, 0xf8, 0xda, 0x0a, 0x42, 0xb0, 0x7f, 0x6e, 0x61, 0xf3, 0xdb, 0xfe, 0xc8, - 0x2c, 0x7c, 0xaa, 0xf1, 0x8f, 0x0a, 0x9f, 0x6e, 0x6a, 0x2d, 0x26, 0x3c, 0xa1, 0x31, 0x27, 0x19, - 0x51, 0x9e, 0x4a, 0xf6, 0x72, 0x36, 0x1a, 0xb8, 0x34, 0x91, 0x0d, 0x3b, 0x84, 0x31, 0xca, 0x64, - 0x9f, 0xf7, 0x4f, 0x5e, 0x6f, 0x37, 0x33, 0xe5, 0xc6, 0x3d, 0x33, 0xcb, 0x95, 0xb3, 0x93, 0x6f, - 0x83, 0x9e, 0x03, 0x30, 0xf2, 0x73, 0x4a, 0xb8, 0x28, 0x87, 0xa3, 0x85, 0xb5, 0xc2, 0x63, 0x05, - 0xc6, 0x6f, 0x0a, 0x68, 0xf3, 0x9c, 0x6a, 0xe9, 0x26, 0xc6, 0x0e, 0x2e, 0x4b, 0x3f, 0x82, 0x27, - 0x83, 0xfe, 0xd5, 0xb9, 0x83, 0x07, 0xe6, 0x99, 0x37, 0x30, 0x47, 0xa3, 0xfe, 0x85, 0xd9, 0x56, - 0xd0, 0x21, 0xb4, 0x7f, 0x30, 0xf1, 0xc8, 0x72, 0x6c, 0x6f, 0x60, 0x8d, 0x06, 0x7d, 0xf7, 0xf4, - 0xbb, 0xb6, 0x8a, 0x9e, 0xc1, 0xf1, 0xb5, 0x3d, 0xba, 0x1e, 0x0e, 0x1d, 0xec, 0x9a, 0x67, 0x55, - 0x86, 0xb5, 0x0c, 0x9a, 0x65, 0xbb, 0x26, 0xb6, 0xfb, 0x57, 0xf9, 0x09, 0xed, 0xba, 0xf1, 0xbb, - 0x02, 0x2f, 0x96, 0x6b, 0xeb, 0x07, 0x6f, 0x09, 0x13, 0x21, 0x27, 0xf7, 0x24, 0x16, 0x56, 0x3c, - 0xa5, 0x59, 0x1d, 0x49, 0x3a, 0x8e, 0xc2, 0x89, 0x77, 0x47, 0x66, 0x12, 0x5a, 0x0b, 0x6b, 0xb9, - 0xe7, 0x92, 0xcc, 0x56, 0xe6, 0x5b, 0x5d, 0x9d, 0xef, 0x6d, 0xdf, 0x8a, 0xf1, 0x2b, 0xe8, 0xc5, - 0x74, 0x9c, 0xd2, 0x80, 0x2c, 0x5c, 0x05, 0xf9, 0x70, 0xbc, 0x22, 0x0f, 0x5e, 0x18, 0x4f, 0xa9, - 0xae, 0x74, 0x6a, 0xdd, 0xe6, 0xc9, 0xe7, 0x0f, 0xf7, 0x6b, 0xa5, 0x26, 0x7c, 0x98, 0x2c, 0x85, - 0x64, 0x5e, 0xe3, 0x35, 0x1c, 0x2d, 0xa7, 0x7e, 0x9f, 0x12, 0x36, 0x43, 0x1f, 0x43, 0xf3, 0x7f, - 0x04, 0x5c, 0x1e, 0xd8, 0xc2, 0x30, 0x67, 0xc0, 0x8d, 0x7f, 0x15, 0xf8, 0x60, 0x6d, 0xaa, 0x24, - 0xb8, 0x8c, 0x48, 0xd9, 0x0a, 0x91, 0xba, 0x56, 0x4e, 0x16, 0xbb, 0x51, 0x5b, 0xee, 0xc6, 0x5a, - 0x59, 0xa8, 0xaf, 0x97, 0x85, 0xf9, 0x53, 0xde, 0xa9, 0x3e, 0xe5, 0x07, 0x25, 0xc7, 0xf8, 0x43, - 0x81, 0xe7, 0x6b, 0x8b, 0x9c, 0x3f, 0xae, 0x57, 0x50, 0xaf, 0x74, 0xe4, 0x93, 0x87, 0x3b, 0x32, - 0x67, 0x83, 0x65, 0x42, 0x56, 0xd5, 0x3d, 0xe1, 0xdc, 0xbf, 0x21, 0x65, 0xe5, 0x2d, 0xac, 0x15, - 0x1e, 0x2b, 0xa8, 0x3e, 0xda, 0xda, 0xc2, 0xa3, 0x35, 0xfe, 0x56, 0xa0, 0xbd, 0xbc, 0xf9, 0x36, - 0xbc, 0x9f, 0xc2, 0x9e, 0x94, 0xc2, 0x39, 0xe7, 0xdd, 0xcc, 0x7c, 0x9c, 0xef, 0x9a, 0x3e, 0xd5, - 0xd7, 0xf6, 0x49, 0x87, 0xbd, 0xe2, 0xfe, 0x05, 0xde, 0xd2, 0x34, 0x12, 0x78, 0xba, 0x2a, 0x28, - 0x52, 0x15, 0xd0, 0x57, 0xd0, 0x28, 0x04, 0x82, 0x17, 0x0c, 0x9f, 0x6d, 0x50, 0xa1, 0x79, 0xec, - 0x23, 0xf8, 0x8c, 0xbf, 0x54, 0x38, 0x5e, 0x3d, 0x32, 0xa1, 0x4c, 0x6c, 0x90, 0xc3, 0x6f, 0x16, - 0xe5, 0xf0, 0xe5, 0x26, 0x39, 0xcc, 0xb6, 0x5a, 0x2b, 0x80, 0xef, 0x02, 0xa5, 0xf1, 0xd3, 0x36, - 0x42, 0x79, 0x00, 0xcd, 0x37, 0xd8, 0xb1, 0x2f, 0xaa, 0xff, 0x12, 0x4b, 0x82, 0xa7, 0x66, 0x3e, - 0xdb, 0x71, 0x3d, 0x6c, 0x5e, 0x58, 0x23, 0xd7, 0xc4, 0xe6, 0x59, 0xbb, 0x66, 0xa4, 0xa0, 0xaf, - 0x16, 0x54, 0xcc, 0xf3, 0x22, 0x57, 0x65, 0x79, 0x2c, 0xbf, 0x86, 0x3d, 0x26, 0x6b, 0xe7, 0xba, - 0x2a, 0xbb, 0xd5, 0x79, 0x0c, 0x12, 0x2e, 0x13, 0xc6, 0xbb, 0x32, 0xf2, 0xcb, 0xff, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x90, 0x90, 0x1b, 0xe9, 0xf4, 0x08, 0x00, 0x00, + // 878 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xeb, 0x44, + 0x17, 0xfd, 0x9c, 0xa4, 0x4d, 0x72, 0x93, 0x2f, 0x4d, 0x47, 0x6d, 0x9f, 0x79, 0xa2, 0x10, 0x0c, + 0x12, 0x51, 0x17, 0x11, 0x2a, 0x12, 0xef, 0x89, 0x15, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x13, + 0x97, 0xa7, 0x27, 0x21, 0x59, 0x8e, 0x3d, 0x69, 0xad, 0xba, 0x1e, 0x33, 0x33, 0x2e, 0xca, 0x8e, + 0x1f, 0xc0, 0x86, 0x2d, 0x1b, 0xfe, 0x02, 0xe2, 0x17, 0x22, 0x8f, 0xed, 0xe0, 0x36, 0x6e, 0x5a, + 0x24, 0x56, 0xf6, 0x9c, 0xb9, 0xf7, 0xce, 0xcc, 0x39, 0xf7, 0x5c, 0x50, 0xe3, 0x84, 0xdf, 0x38, + 0x11, 0x15, 0xc1, 0x22, 0xf0, 0x5c, 0x11, 0xd0, 0x88, 0x8f, 0x62, 0x46, 0x05, 0x45, 0x2d, 0xf9, + 0x99, 0x27, 0x0b, 0xed, 0x8f, 0x06, 0x7c, 0x38, 0x4d, 0xf8, 0x8d, 0x59, 0x8a, 0xc2, 0xe4, 0x3a, + 0xe0, 0x82, 0xc9, 0x7f, 0x64, 0x01, 0x08, 0x7a, 0x4b, 0x22, 0x47, 0x2c, 0x63, 0xa2, 0x2a, 0x03, + 0x65, 0xd8, 0x3b, 0xfe, 0x62, 0x54, 0xe4, 0x8f, 0x36, 0xe5, 0x8e, 0xec, 0x34, 0xd1, 0x5e, 0xc6, + 0x04, 0xb7, 0x45, 0xf1, 0x8b, 0x3e, 0x81, 0xae, 0x4f, 0xee, 0x03, 0x8f, 0x38, 0x12, 0x53, 0x6b, + 0x03, 0x65, 0xd8, 0xc6, 0x9d, 0x0c, 0x93, 0x19, 0xe8, 0x73, 0xd8, 0x09, 0x22, 0x2e, 0xdc, 0x30, + 0x94, 0x75, 0x9c, 0xc0, 0x57, 0xeb, 0x32, 0xaa, 0x57, 0x86, 0x0d, 0x3f, 0xad, 0xe5, 0x7a, 0x1e, + 0xe1, 0x3c, 0xaf, 0xd5, 0xc8, 0x6a, 0x65, 0x58, 0x56, 0x4b, 0x85, 0x26, 0x89, 0xdc, 0x79, 0x48, + 0x7c, 0x75, 0x6b, 0xa0, 0x0c, 0x5b, 0xb8, 0x58, 0xa6, 0x3b, 0xf7, 0x84, 0xf1, 0x80, 0x46, 0xea, + 0xf6, 0x40, 0x19, 0x36, 0x70, 0xb1, 0x44, 0x43, 0xe8, 0xbb, 0x61, 0x48, 0x7f, 0x26, 0xbe, 0x73, + 0x4b, 0x96, 0x4e, 0x18, 0x70, 0xa1, 0x36, 0x07, 0xf5, 0x61, 0x17, 0xf7, 0x72, 0xfc, 0x82, 0x2c, + 0x2f, 0x03, 0x2e, 0xd0, 0x11, 0xec, 0xce, 0x43, 0xea, 0xdd, 0x12, 0xdf, 0xf1, 0x6e, 0x5c, 0x91, + 0x85, 0xb6, 0x64, 0xe8, 0x4e, 0xbe, 0x71, 0x72, 0xe3, 0x0a, 0x19, 0xfb, 0x11, 0x40, 0x12, 0x31, + 0xc9, 0x0f, 0x61, 0x6a, 0x5b, 0x5e, 0xa6, 0x84, 0xa0, 0x3d, 0xd8, 0xba, 0x66, 0x6e, 0x24, 0x54, + 0x18, 0x28, 0xc3, 0x2e, 0xce, 0x16, 0xe8, 0x0d, 0xa8, 0xf2, 0x4c, 0x67, 0xc1, 0xe8, 0x9d, 0xe3, + 0xd1, 0x48, 0xb8, 0x9e, 0xe0, 0x0e, 0x8d, 0xc2, 0xa5, 0xda, 0x91, 0x35, 0xf6, 0xe5, 0xfe, 0x19, + 0xa3, 0x77, 0x27, 0xf9, 0xae, 0x15, 0x85, 0x4b, 0xed, 0x0c, 0xda, 0x2b, 0xfe, 0xd1, 0x01, 0xa0, + 0x2b, 0xf3, 0xc2, 0xb4, 0xde, 0x99, 0x8e, 0x6d, 0x5d, 0xe8, 0xa6, 0x63, 0xbf, 0x9f, 0xea, 0xfd, + 0xff, 0xa1, 0xff, 0x43, 0x7b, 0x3c, 0xcd, 0xb1, 0xbe, 0x82, 0x10, 0xf4, 0xce, 0x0c, 0xac, 0x7f, + 0x3b, 0x9e, 0xe9, 0x39, 0x56, 0xd3, 0xfe, 0xaa, 0xc1, 0x67, 0x9b, 0x54, 0xc6, 0x84, 0xc7, 0x34, + 0xe2, 0x24, 0xe5, 0x93, 0x27, 0x92, 0x79, 0xd9, 0x26, 0x2d, 0x5c, 0x2c, 0x91, 0x09, 0x5b, 0x84, + 0x31, 0xca, 0xa4, 0xd6, 0xbd, 0xe3, 0xb7, 0x2f, 0x6b, 0x9f, 0xa2, 0xf0, 0x48, 0x4f, 0x73, 0x65, + 0x1b, 0x65, 0x65, 0xd0, 0x21, 0x00, 0x23, 0x3f, 0x25, 0x84, 0x8b, 0xa2, 0x35, 0xba, 0xb8, 0x9d, + 0x23, 0x86, 0xaf, 0xfd, 0xa2, 0x40, 0x7b, 0x95, 0x53, 0x7e, 0xba, 0x8e, 0xb1, 0x85, 0x8b, 0xa7, + 0xef, 0xc3, 0xee, 0x64, 0x7c, 0x79, 0x66, 0xe1, 0x89, 0x7e, 0xea, 0x4c, 0xf4, 0xd9, 0x6c, 0x7c, + 0xae, 0xf7, 0x15, 0xb4, 0x07, 0xfd, 0x1f, 0x74, 0x3c, 0x33, 0x2c, 0xd3, 0x99, 0x18, 0xb3, 0xc9, + 0xd8, 0x3e, 0xf9, 0xae, 0x5f, 0x43, 0xaf, 0xe1, 0xe0, 0xca, 0x9c, 0x5d, 0x4d, 0xa7, 0x16, 0xb6, + 0xf5, 0xd3, 0x32, 0x87, 0xf5, 0x94, 0x34, 0xc3, 0xb4, 0x75, 0x6c, 0x8e, 0x2f, 0xb3, 0x13, 0xfa, + 0x0d, 0x2d, 0x01, 0x35, 0x17, 0xe3, 0x84, 0xfa, 0x64, 0xec, 0xdf, 0x13, 0x26, 0x02, 0x4e, 0xee, + 0x48, 0x24, 0xd0, 0x7b, 0x38, 0x58, 0x33, 0xa6, 0x13, 0x44, 0x0b, 0xaa, 0x2a, 0x83, 0xfa, 0xb0, + 0x73, 0xfc, 0xe9, 0xd3, 0xf4, 0x7c, 0x9f, 0x10, 0xb6, 0x34, 0xa2, 0x05, 0xc5, 0x7b, 0xf1, 0xa3, + 0xad, 0x14, 0xd5, 0xde, 0xc2, 0x7e, 0x65, 0x0a, 0xfa, 0x18, 0x3a, 0x71, 0x32, 0x0f, 0x03, 0x2f, + 0x6d, 0x68, 0x2e, 0x0f, 0xea, 0x62, 0xc8, 0xa0, 0x0b, 0xb2, 0xe4, 0xda, 0xaf, 0x35, 0xf8, 0xe0, + 0xc9, 0xd3, 0xd6, 0x7c, 0xa6, 0xac, 0xfb, 0xac, 0xc2, 0xb3, 0xb5, 0x4a, 0xcf, 0x1e, 0x02, 0xfc, + 0x73, 0x95, 0x42, 0xbc, 0xd5, 0x4d, 0x2a, 0xbd, 0xd7, 0xa8, 0xf4, 0xde, 0xca, 0x2f, 0x5b, 0x65, + 0xbf, 0x3c, 0xed, 0xea, 0x23, 0xd8, 0xe5, 0x84, 0xdd, 0x13, 0xe6, 0x94, 0xce, 0x6f, 0xca, 0xdc, + 0x9d, 0x6c, 0x63, 0x5a, 0xdc, 0x42, 0xfb, 0x4d, 0x81, 0xc3, 0x4a, 0x3a, 0x56, 0xdd, 0xfe, 0x06, + 0x1a, 0xff, 0x56, 0x33, 0x99, 0x90, 0xbe, 0xff, 0x8e, 0x70, 0xee, 0x5e, 0x93, 0x82, 0xa3, 0x2e, + 0x6e, 0xe7, 0x88, 0xe1, 0x97, 0x5d, 0x54, 0x7f, 0xe0, 0x22, 0xed, 0x4f, 0x05, 0xfa, 0x8f, 0x8b, + 0xbf, 0x44, 0x99, 0x57, 0xd0, 0x94, 0xb3, 0x69, 0xa5, 0xc8, 0x76, 0xba, 0x7c, 0x5e, 0x89, 0x0a, + 0x45, 0x1b, 0x95, 0x8a, 0xaa, 0xd0, 0xcc, 0xef, 0x9f, 0x4b, 0x51, 0x2c, 0xb5, 0x18, 0x5e, 0xad, + 0x3b, 0x5c, 0xda, 0x14, 0x7d, 0x05, 0xad, 0xdc, 0xb1, 0x3c, 0xe7, 0xf0, 0xf5, 0x86, 0xb1, 0xb0, + 0x8a, 0x7d, 0x86, 0x3e, 0xed, 0xf7, 0x1a, 0x1c, 0xac, 0x1f, 0x19, 0x53, 0x26, 0x36, 0xcc, 0xa7, + 0x6f, 0x1e, 0xce, 0xa7, 0xa3, 0x4d, 0xf3, 0x29, 0x2d, 0x55, 0x39, 0x91, 0xfe, 0x0b, 0x2a, 0xb5, + 0x1f, 0x5f, 0x32, 0xb9, 0x76, 0xa0, 0xf3, 0x0e, 0x5b, 0xe6, 0x79, 0x79, 0x6c, 0x3f, 0x9a, 0x40, + 0xb5, 0x14, 0x33, 0x2d, 0xdb, 0xc1, 0xfa, 0xb9, 0x31, 0xb3, 0x75, 0xac, 0x9f, 0xf6, 0xeb, 0xe9, + 0x54, 0x5a, 0x7f, 0x50, 0xde, 0xcf, 0x0f, 0x79, 0x55, 0x1e, 0xb7, 0xe5, 0xd7, 0xd0, 0x64, 0xf2, + 0xed, 0x5c, 0xad, 0x49, 0xb5, 0x06, 0xcf, 0x91, 0x84, 0x8b, 0x84, 0xf9, 0xb6, 0x8c, 0xfc, 0xf2, + 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xe8, 0xb9, 0x16, 0x90, 0x08, 0x00, 0x00, } diff --git a/protocol/protobuf/push_notifications.proto b/protocol/protobuf/push_notifications.proto index 6642c0bc2..ce4ec1c45 100644 --- a/protocol/protobuf/push_notifications.proto +++ b/protocol/protobuf/push_notifications.proto @@ -9,12 +9,12 @@ message PushNotificationRegistration { FIREBASE_TOKEN = 2; } TokenType token_type = 1; - string token = 2; + string device_token = 2; string installation_id = 3; string access_token = 4; bool enabled = 5; uint64 version = 6; - repeated bytes allowed_user_list = 7; + repeated bytes allowed_key_list = 7; repeated bytes blocked_chat_list = 8; bool unregister = 9; bytes grant = 10; @@ -35,14 +35,8 @@ message PushNotificationRegistrationResponse { } } -message PushNotificationAdvertisementInfo { - bytes public_key = 1; - string access_token = 2; - string installation_id = 3; -} - message ContactCodeAdvertisement { - repeated PushNotificationAdvertisementInfo push_notification_info = 1; + repeated PushNotificationQueryInfo push_notification_info = 1; } message PushNotificationQuery { @@ -53,9 +47,10 @@ message PushNotificationQueryInfo { string access_token = 1; string installation_id = 2; bytes public_key = 3; - repeated bytes allowed_user_list = 4; + repeated bytes allowed_key_list = 4; bytes grant = 5; uint64 version = 6; + bytes server_public_key = 7; } message PushNotificationQueryResponse { diff --git a/protocol/push_notification_test.go b/protocol/push_notification_test.go index 1bf46cc22..35e950016 100644 --- a/protocol/push_notification_test.go +++ b/protocol/push_notification_test.go @@ -17,12 +17,17 @@ import ( "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/push_notification_client" - "github.com/status-im/status-go/protocol/push_notification_server" + "github.com/status-im/status-go/protocol/pushnotificationclient" + "github.com/status-im/status-go/protocol/pushnotificationserver" "github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/waku" ) +const ( + bob1DeviceToken = "token-1" + bob2DeviceToken = "token-2" +) + func TestMessengerPushNotificationSuite(t *testing.T) { suite.Run(t, new(MessengerPushNotificationSuite)) } @@ -60,9 +65,6 @@ func (s *MessengerPushNotificationSuite) TearDownTest() { } func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger { - tmpFile, err := ioutil.TempFile("", "") - s.Require().NoError(err) - m, err := NewMessenger( privateKey, &testNode{shh: shh}, @@ -74,8 +76,6 @@ func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Waku, err = m.Init() s.Require().NoError(err) - s.tmpFiles = append(s.tmpFiles, tmpFile) - return m } @@ -86,7 +86,7 @@ func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Waku, pri options := []Option{ WithCustomLogger(s.logger), WithMessagesPersistenceEnabled(), - WithDatabaseConfig(tmpFile.Name(), "some-key"), + WithDatabaseConfig(tmpFile.Name(), ""), WithDatasync(), } return s.newMessengerWithOptions(shh, privateKey, options) @@ -99,14 +99,12 @@ func (s *MessengerPushNotificationSuite) newMessenger(shh types.Waku) *Messenger return s.newMessengerWithKey(s.shh, privateKey) } -func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku) *Messenger { - privateKey, err := crypto.GenerateKey() - s.Require().NoError(err) +func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger { tmpFile, err := ioutil.TempFile("", "") s.Require().NoError(err) - serverConfig := &push_notification_server.Config{ + serverConfig := &pushnotificationserver.Config{ Logger: s.logger, Identity: privateKey, } @@ -123,12 +121,13 @@ func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Wak func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { - bob1DeviceToken := "token-1" - bob2DeviceToken := "token-2" - bob1 := s.m bob2 := s.newMessengerWithKey(s.shh, s.m.identity) - server := s.newPushNotificationServer(s.shh) + + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + server := s.newPushNotificationServer(s.shh, serverKey) + alice := s.newMessenger(s.shh) // start alice and enable sending push notifications s.Require().NoError(alice.Start()) @@ -136,7 +135,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { bobInstallationIDs := []string{bob1.installationID, bob2.installationID} // Register bob1 - err := bob1.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) @@ -166,7 +165,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NoError(err) // Register bob2 - err = bob2.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) @@ -207,7 +206,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { messageID, err := hex.DecodeString(messageIDString[2:]) s.Require().NoError(err) - var info []*push_notification_client.PushNotificationInfo + var info []*pushnotificationclient.PushNotificationInfo err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -233,7 +232,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NoError(err) // Check we have replies for both bob1 and bob2 - var bob1Info, bob2Info *push_notification_client.PushNotificationInfo + var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo if info[0].AccessToken == bob1Servers[0].AccessToken { bob1Info = info[0] @@ -259,7 +258,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 2) - var sentNotification *push_notification_client.SentNotification + var sentNotification *pushnotificationclient.SentNotification err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -289,10 +288,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() { func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() { - bobDeviceToken := "token-1" - bob := s.m - server := s.newPushNotificationServer(s.shh) + + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + server := s.newPushNotificationServer(s.shh, serverKey) + alice := s.newMessenger(s.shh) // start alice and enable push notifications s.Require().NoError(alice.Start()) @@ -300,7 +301,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO bobInstallationIDs := []string{bob.installationID} // Register bob - err := bob.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) // Add alice has a contact @@ -317,7 +318,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO err = bob.EnablePushNotificationsFromContactsOnly() s.Require().NoError(err) - err = bob.RegisterForPushNotifications(context.Background(), bobDeviceToken) + err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken) s.Require().NoError(err) // Pull servers and check we registered @@ -355,7 +356,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO messageID, err := hex.DecodeString(messageIDString[2:]) s.Require().NoError(err) - var info []*push_notification_client.PushNotificationInfo + var info []*pushnotificationclient.PushNotificationInfo err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -389,7 +390,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 1) - var sentNotification *push_notification_client.SentNotification + var sentNotification *pushnotificationclient.SentNotification err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -419,10 +420,12 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { - bobDeviceToken := "token-1" - bob := s.m - server := s.newPushNotificationServer(s.shh) + + serverKey, err := crypto.GenerateKey() + s.Require().NoError(err) + server := s.newPushNotificationServer(s.shh, serverKey) + alice := s.newMessenger(s.shh) // another contact to invalidate the token frank := s.newMessenger(s.shh) @@ -432,7 +435,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { bobInstallationIDs := []string{bob.installationID} // Register bob - err := bob.AddPushNotificationServer(context.Background(), &server.identity.PublicKey) + err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) s.Require().NoError(err) // Add alice has a contact @@ -459,7 +462,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { err = bob.EnablePushNotificationsFromContactsOnly() s.Require().NoError(err) - err = bob.RegisterForPushNotifications(context.Background(), bobDeviceToken) + err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken) s.Require().NoError(err) // Pull servers and check we registered @@ -495,7 +498,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { s.Require().NoError(err) // We check that alice retrieves the info from the server - var info []*push_notification_client.PushNotificationInfo + var info []*pushnotificationclient.PushNotificationInfo err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -603,7 +606,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { s.Require().NotNil(retrievedNotificationInfo) s.Require().Len(retrievedNotificationInfo, 1) - var sentNotification *push_notification_client.SentNotification + var sentNotification *pushnotificationclient.SentNotification err = tt.RetryWithBackOff(func() error { _, err = server.RetrieveAll() if err != nil { @@ -630,3 +633,165 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() { s.Require().NoError(alice.Shutdown()) s.Require().NoError(server.Shutdown()) } + +// Here bob acts as his own server +func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer() { + bob1 := s.m + server := s.newPushNotificationServer(s.shh, s.m.identity) + bob2 := server + alice := s.newMessenger(s.shh) + // start alice and enable sending push notifications + s.Require().NoError(alice.Start()) + s.Require().NoError(alice.EnableSendingPushNotifications()) + bobInstallationIDs := []string{bob1.installationID, bob2.installationID} + + // Register bob1 + err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken) + + // Pull servers and check we registered + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob1.RetrieveAll() + if err != nil { + return err + } + registered, err := bob1.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bob1Servers, err := bob1.GetPushNotificationServers() + s.Require().NoError(err) + + // Register bob2 + err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey) + s.Require().NoError(err) + + err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken) + s.Require().NoError(err) + + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = bob2.RetrieveAll() + if err != nil { + return err + } + + registered, err := bob2.RegisteredForPushNotifications() + if err != nil { + return err + } + if !registered { + return errors.New("not registered") + } + return nil + }) + // Make sure we receive it + s.Require().NoError(err) + bob2Servers, err := bob2.GetPushNotificationServers() + s.Require().NoError(err) + + // Create one to one chat & send message + pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey)) + chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport) + s.Require().NoError(alice.SaveChat(&chat)) + inputMessage := buildTestMessage(chat) + response, err := alice.SendChatMessage(context.Background(), inputMessage) + s.Require().NoError(err) + messageIDString := response.Messages[0].ID + messageID, err := hex.DecodeString(messageIDString[2:]) + s.Require().NoError(err) + + var info []*pushnotificationclient.PushNotificationInfo + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + + info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + if err != nil { + return err + } + // Check we have replies for both bob1 and bob2 + if len(info) != 2 { + return errors.New("info not fetched") + } + return nil + + }) + + s.Require().NoError(err) + + // Check we have replies for both bob1 and bob2 + var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo + + if info[0].AccessToken == bob1Servers[0].AccessToken { + bob1Info = info[0] + bob2Info = info[1] + } else { + bob2Info = info[0] + bob1Info = info[1] + } + + s.Require().NotNil(bob1Info) + s.Require().Equal(bob1.installationID, bob1Info.InstallationID) + s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken) + s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey) + + s.Require().NotNil(bob2Info) + s.Require().Equal(bob2.installationID, bob2Info.InstallationID) + s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken) + s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey) + + retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs) + + s.Require().NoError(err) + s.Require().NotNil(retrievedNotificationInfo) + s.Require().Len(retrievedNotificationInfo, 2) + + var sentNotification *pushnotificationclient.SentNotification + err = tt.RetryWithBackOff(func() error { + _, err = server.RetrieveAll() + if err != nil { + return err + } + _, err = alice.RetrieveAll() + if err != nil { + return err + } + sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID) + if err != nil { + return err + } + if sentNotification == nil { + return errors.New("sent notification not found") + } + if !sentNotification.Success { + return errors.New("sent notification not successul") + } + return nil + }) + s.Require().NoError(err) + s.Require().NoError(bob2.Shutdown()) + s.Require().NoError(alice.Shutdown()) +} diff --git a/protocol/push_notification_client/client.go b/protocol/pushnotificationclient/client.go similarity index 70% rename from protocol/push_notification_client/client.go rename to protocol/pushnotificationclient/client.go index 08d9f0ce4..313d5f7ec 100644 --- a/protocol/push_notification_client/client.go +++ b/protocol/pushnotificationclient/client.go @@ -1,4 +1,4 @@ -package push_notification_client +package pushnotificationclient import ( "bytes" @@ -17,15 +17,33 @@ import ( "github.com/golang/protobuf/proto" "github.com/google/uuid" + "go.uber.org/zap" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" - "go.uber.org/zap" ) +// How does sending notifications work? +// 1) Every time a message is scheduled for sending, it will be received on a channel. +// we keep track on whether we should send a push notification for this message. +// 2) Every time a message is dispatched, we check whether we should send a notification. +// If so, we query the user info if necessary, check which installations we should be targeting +// and notify the server if we have information about the user (i.e a token). +// The logic is complicated by the fact that sometimes messages are batched together (datasync) +// and the fact that sometimes we send messages to all devices (dh messages). +// 3) The server will notify us if the wrong token is used, in which case a loop will be started that +// will re-query and re-send the notification, up to a maximum. + +// How does registering works? +// We register with the server asynchronously, through a loop, that will try to make sure that +// we have registered with all the servers added, until eventually it gives up. + +// A lot of the logic is complicated by the fact that waku/whisper is not req/response, so we just fire a message +// hoping to get a reply at some later stages. + const encryptedPayloadKeyLength = 16 const accessTokenKeyLength = 16 const staleQueryTimeInSeconds = 86400 @@ -95,9 +113,9 @@ type Config struct { // RemoteNotificationsEnabled is whether we should register with a remote server for push notifications RemoteNotificationsEnabled bool - // allowyFromContactsOnly indicates whether we should be receiving push notifications + // AllowyFromContactsOnly indicates whether we should be receiving push notifications // only from contacts - allowFromContactsOnly bool + AllowFromContactsOnly bool // InstallationID is the installation-id for this device InstallationID string @@ -110,8 +128,8 @@ type Config struct { type Client struct { persistence *Persistence - quit chan struct{} - config *Config + + config *Config // lastPushNotificationRegistration is the latest known push notification version lastPushNotificationRegistration *protobuf.PushNotificationRegistration @@ -133,8 +151,10 @@ type Client struct { // registrationLoopQuitChan is a channel to indicate to the registration loop that should be terminating registrationLoopQuitChan chan struct{} - // resendingLoopQuitChan is a channel to indicate to the send loop that shoudl be terminating + // resendingLoopQuitChan is a channel to indicate to the send loop that should be terminating resendingLoopQuitChan chan struct{} + + quit chan struct{} } func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client { @@ -147,19 +167,340 @@ func New(persistence *Persistence, config *Config, processor *common.MessageProc } } +func (c *Client) Start() error { + if c.messageProcessor == nil { + return errors.New("can't start, missing message processor") + } + + err := c.loadLastPushNotificationRegistration() + if err != nil { + return err + } + + c.subscribeForSentMessages() + c.subscribeForScheduledMessages() + c.startRegistrationLoop() + c.startResendingLoop() + + return nil +} + +func (c *Client) Stop() error { + close(c.quit) + c.stopRegistrationLoop() + c.stopResendingLoop() + return nil +} + +// Unregister unregisters from all the servers +func (c *Client) Unregister() error { + // stop registration loop + c.stopRegistrationLoop() + + registration := c.buildPushNotificationUnregisterMessage() + err := c.saveLastPushNotificationRegistration(registration, nil) + if err != nil { + return err + } + + // reset servers + err = c.resetServers() + if err != nil { + return err + } + + // and asynchronously register + c.startRegistrationLoop() + return nil +} + +// Registered returns true if we registered with all the servers +func (c *Client) Registered() (bool, error) { + servers, err := c.persistence.GetServers() + if err != nil { + return false, err + } + + for _, s := range servers { + if !s.Registered { + return false, nil + } + } + + return true, nil +} + +func (c *Client) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) { + return c.persistence.GetSentNotification(hashedPublicKey, installationID, messageID) +} + +func (c *Client) GetServers() ([]*PushNotificationServer, error) { + return c.persistence.GetServers() +} + +func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.Logger.Debug("re-registering") + if len(c.deviceToken) == 0 { + c.config.Logger.Info("no device token, not registering") + return nil + } + + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) +} + +// Register registers with all the servers +func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + // stop registration loop + c.stopRegistrationLoop() + + // reset servers + err := c.resetServers() + if err != nil { + return err + } + + c.deviceToken = deviceToken + + registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) + if err != nil { + return err + } + + err = c.saveLastPushNotificationRegistration(registration, contactIDs) + if err != nil { + return err + } + + c.startRegistrationLoop() + + return nil +} + +// HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database +func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, response protobuf.PushNotificationRegistrationResponse) error { + c.config.Logger.Debug("received push notification registration response", zap.Any("response", response)) + + // Not successful ignore for now + if !response.Success { + return errors.New("response was not successful") + } + + servers, err := c.persistence.GetServersByPublicKey([]*ecdsa.PublicKey{publicKey}) + if err != nil { + return err + } + + // we haven't registered with this server + if len(servers) != 1 { + return errors.New("not registered with this server, ignoring") + } + + server := servers[0] + server.Registered = true + server.RegisteredAt = time.Now().Unix() + + return c.persistence.UpsertServer(server) +} + +// HandlePushNotificationQueryResponse should update the data in the database for a given user +func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { + c.config.Logger.Debug("received push notification query response", zap.Any("response", response)) + if len(response.Info) == 0 { + return errors.New("empty response from the server") + } + + // get the public key associated with this query + publicKey, err := c.persistence.GetQueryPublicKey(response.MessageId) + if err != nil { + return err + } + if publicKey == nil { + c.config.Logger.Debug("query not found") + return nil + } + + var pushNotificationInfo []*PushNotificationInfo + for _, info := range response.Info { + // make sure the public key matches + if !bytes.Equal(info.PublicKey, common.HashPublicKey(publicKey)) { + c.config.Logger.Warn("reply for different key, ignoring") + continue + } + + accessToken := info.AccessToken + + // the user wants notification from contacts only, try to decrypt the access token to see if we are in their contacts + if len(accessToken) == 0 && len(info.AllowedKeyList) != 0 { + accessToken = c.handleAllowedKeyList(publicKey, info.AllowedKeyList) + + } + + // no luck + if len(accessToken) == 0 { + c.config.Logger.Debug("not in the allowed key list") + continue + } + + // We check the user has allowed this server to store this particular + // access token, otherwise anyone could reply with a fake token + // and receive notifications for a user + if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, accessToken); err != nil { + c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err)) + continue + } + + pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ + PublicKey: publicKey, + ServerPublicKey: serverPublicKey, + AccessToken: accessToken, + InstallationID: info.InstallationId, + Version: info.Version, + RetrievedAt: time.Now().Unix(), + }) + + } + + err = c.persistence.SavePushNotificationInfo(pushNotificationInfo) + if err != nil { + c.config.Logger.Error("failed to save push notifications", zap.Error(err)) + return err + } + + return nil +} + +// HandlePushNotificationResponse should set the request as processed +func (c *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { + messageID := response.MessageId + c.config.Logger.Debug("received response for", zap.Binary("message-id", messageID)) + for _, report := range response.Reports { + c.config.Logger.Debug("received response", zap.Any("report", report)) + err := c.persistence.UpdateNotificationResponse(messageID, report) + if err != nil { + return err + } + } + + // Restart resending loop, in case we need to resend some notifications + c.stopResendingLoop() + c.startResendingLoop() + return nil +} + +func (c *Client) RemovePushNotificationServer(publicKey *ecdsa.PublicKey) error { + c.config.Logger.Debug("removing push notification server", zap.Any("public-key", publicKey)) + //TODO: this needs implementing. It requires unregistering from the server and + // likely invalidate the device token of the user + return errors.New("not implemented") +} + +func (c *Client) AddPushNotificationsServer(publicKey *ecdsa.PublicKey) error { + c.config.Logger.Debug("adding push notifications server", zap.Any("public-key", publicKey)) + currentServers, err := c.persistence.GetServers() + if err != nil { + return err + } + + for _, server := range currentServers { + if common.IsPubKeyEqual(server.PublicKey, publicKey) { + return errors.New("push notification server already added") + } + } + + err = c.persistence.UpsertServer(&PushNotificationServer{ + PublicKey: publicKey, + }) + if err != nil { + return err + } + + if c.config.RemoteNotificationsEnabled { + c.startRegistrationLoop() + } + return nil +} + +func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) { + if len(installationIDs) == 0 { + return c.persistence.GetPushNotificationInfoByPublicKey(publicKey) + } + return c.persistence.GetPushNotificationInfo(publicKey, installationIDs) +} + +func (c *Client) EnableSending() { + c.config.SendEnabled = true +} + +func (c *Client) DisableSending() { + c.config.SendEnabled = false +} + +func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.AllowFromContactsOnly = true + if c.lastPushNotificationRegistration != nil { + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) + } + return nil +} + +func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { + c.config.AllowFromContactsOnly = false + if c.lastPushNotificationRegistration != nil { + return c.Register(c.deviceToken, contactIDs, mutedChatIDs) + } + return nil +} + +func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { + c, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(c) + if err != nil { + return nil, err + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(reader, nonce); err != nil { + return nil, err + } + + return gcm.Seal(nonce, nonce, plaintext, nil), nil +} + +func (c *Client) encryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { + sharedKey, err := c.generateSharedKey(publicKey) + if err != nil { + return nil, err + } + + return common.Encrypt(payload, sharedKey, c.reader) +} + +func (c *Client) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { + return ecies.ImportECDSA(c.config.Identity).GenerateShared( + ecies.ImportECDSAPublic(publicKey), + encryptedPayloadKeyLength, + encryptedPayloadKeyLength, + ) +} + +// subscribeForSentMessages subscribes for newly sent messages so we can check if we need to send a push notification func (c *Client) subscribeForSentMessages() { go func() { - c.config.Logger.Info("subscribing for messages") - subscription := c.messageProcessor.Subscribe() + c.config.Logger.Debug("subscribing for sent messages") + subscription := c.messageProcessor.SubscribeToSentMessages() for { select { case m, more := <-subscription: if !more { - c.config.Logger.Info("no more") + c.config.Logger.Debug("no more sent messages, quitting") return } - c.config.Logger.Info("handling message sent") - if err := c.HandleMessageSent(m); err != nil { + c.config.Logger.Debug("handling message sent") + if err := c.handleMessageSent(m); err != nil { c.config.Logger.Error("failed to handle message", zap.Error(err)) } case <-c.quit: @@ -167,9 +508,32 @@ func (c *Client) subscribeForSentMessages() { } } }() - } +// subscribeForScheduledMessages subscribes for messages scheduler for dispatch +func (c *Client) subscribeForScheduledMessages() { + go func() { + c.config.Logger.Debug("subscribing for scheduled messages") + subscription := c.messageProcessor.SubscribeToScheduledMessages() + for { + select { + case m, more := <-subscription: + if !more { + c.config.Logger.Debug("no more scheduled messages, quitting") + return + } + c.config.Logger.Debug("handling message scheduled") + if err := c.handleMessageScheduled(m); err != nil { + c.config.Logger.Error("failed to handle message", zap.Error(err)) + } + case <-c.quit: + return + } + } + }() +} + +// loadLastPushNotificationRegistration loads from the database the last registration func (c *Client) loadLastPushNotificationRegistration() error { lastRegistration, lastContactIDs, err := c.persistence.GetLastPushNotificationRegistration() if err != nil { @@ -180,10 +544,11 @@ func (c *Client) loadLastPushNotificationRegistration() error { } c.lastContactIDs = lastContactIDs c.lastPushNotificationRegistration = lastRegistration - c.deviceToken = lastRegistration.Token + c.deviceToken = lastRegistration.DeviceToken return nil } + func (c *Client) stopRegistrationLoop() { // stop old registration loop if c.registrationLoopQuitChan != nil { @@ -203,51 +568,43 @@ func (c *Client) stopResendingLoop() { func (c *Client) startRegistrationLoop() { c.stopRegistrationLoop() c.registrationLoopQuitChan = make(chan struct{}) - go c.registrationLoop() + go func() { + err := c.registrationLoop() + if err != nil { + c.config.Logger.Error("registration loop exited with an error", zap.Error(err)) + } + }() } func (c *Client) startResendingLoop() { c.stopResendingLoop() c.resendingLoopQuitChan = make(chan struct{}) - go c.resendingLoop() -} - -func (c *Client) Start() error { - if c.messageProcessor == nil { - return errors.New("can't start, missing message processor") - } - - err := c.loadLastPushNotificationRegistration() - if err != nil { - return err - } - c.subscribeForSentMessages() - c.startRegistrationLoop() - c.startResendingLoop() - - return nil -} - -func (c *Client) Stop() error { - close(c.quit) - c.stopRegistrationLoop() - c.stopResendingLoop() - return nil + go func() { + err := c.resendingLoop() + if err != nil { + c.config.Logger.Error("resending loop exited with an error", zap.Error(err)) + } + }() } +// queryNotificationInfo will block and query for the client token, if force is set it +// will ignore the cool off period func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) error { - c.config.Logger.Info("Getting queried at") + c.config.Logger.Debug("retrieving queried at") + // Check if we queried recently queriedAt, err := c.persistence.GetQueriedAt(publicKey) if err != nil { + c.config.Logger.Error("failed to retrieve queried at", zap.Error(err)) return err } - c.config.Logger.Info("checking if querying necessary") + c.config.Logger.Debug("checking if querying necessary") + // Naively query again if too much time has passed. // Here it might not be necessary if force || time.Now().Unix()-queriedAt > staleQueryTimeInSeconds { - c.config.Logger.Info("querying info") - err := c.QueryPushNotificationInfo(publicKey) + c.config.Logger.Debug("querying info") + err := c.queryPushNotificationInfo(publicKey) if err != nil { c.config.Logger.Error("could not query pn info", zap.Error(err)) return err @@ -259,21 +616,32 @@ func (c *Client) queryNotificationInfo(publicKey *ecdsa.PublicKey, force bool) e // The only time we are 100% certain that we can proceed is // when we have non-stale info for each device, but // most devices are not going to be registered, so we'd still - // have to wait teh maximum amount of time allowed. + // have to wait the maximum amount of time allowed. + // A better way to handle this is to set a maximum timer of say + // 3 seconds, but act at a tick every 200ms. + // That way we still are able to batch multiple push notifications + // but we don't have to wait every time 3 seconds, which is wasteful + // This probably will have to be addressed before released time.Sleep(3 * time.Second) - } return nil } -func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { - c.config.Logger.Info("sent message", zap.Any("sent message", sentMessage)) +// handleMessageSent is called every time a message is sent. It will check if +// we need to notify on the message, and if so it will try to dispatch a push notification +// messages might be batched, if coming from datasync for example. +func (c *Client) handleMessageSent(sentMessage *common.SentMessage) error { + c.config.Logger.Debug("sent messages", zap.Any("messageIDs", sentMessage.MessageIDs)) + + // Ignore if we are not sending notifications if !c.config.SendEnabled { - c.config.Logger.Info("send not enabled, ignoring") + c.config.Logger.Debug("send not enabled, ignoring") return nil } + publicKey := sentMessage.PublicKey - // Check we track this messages fist + + // Collect the messageIDs we want to notify on var trackedMessageIDs [][]byte for _, messageID := range sentMessage.MessageIDs { @@ -288,18 +656,19 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { // Nothing to do if len(trackedMessageIDs) == 0 { - c.config.Logger.Info("nothing to do") + c.config.Logger.Debug("nothing to do for", zap.Any("messageIDs", sentMessage.MessageIDs)) return nil } + // sendToAllDevices indicates whether the message has been sent using public key encryption only + // i.e not through the double ratchet. In that case, any device will have received it. sendToAllDevices := len(sentMessage.Spec.Installations) == 0 var installationIDs []string anyActionableMessage := sendToAllDevices - c.config.Logger.Info("send to all devices", zap.Bool("send to all", sendToAllDevices)) - // Collect installationIDs + // Check if we should be notifiying those installations for _, messageID := range trackedMessageIDs { for _, installation := range sentMessage.Spec.Installations { installationID := installation.ID @@ -316,22 +685,23 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { // Is there anything we should be notifying on? if !anyActionableMessage { - c.config.Logger.Info("no actionable installation IDs") + c.config.Logger.Debug("no actionable installation IDs") return nil } - c.config.Logger.Info("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) + c.config.Logger.Debug("actionable messages", zap.Any("message-ids", trackedMessageIDs), zap.Any("installation-ids", installationIDs)) + // we send the notifications and return the info of the devices notified infos, err := c.sendNotification(publicKey, installationIDs, trackedMessageIDs[0]) if err != nil { return err } - // Mark message as sent, this is at-most-once semantic - // for all messageIDs + + // mark message as sent so we don't notify again for _, i := range infos { for _, messageID := range trackedMessageIDs { - c.config.Logger.Info("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) + c.config.Logger.Debug("marking as sent ", zap.Binary("mid", messageID), zap.String("id", i.InstallationID)) if err := c.notifiedOn(publicKey, i.InstallationID, messageID); err != nil { return err } @@ -342,21 +712,28 @@ func (c *Client) HandleMessageSent(sentMessage *common.SentMessage) error { return nil } -// NotifyOnMessageID keeps track of the message to make sure we notify on it -func (c *Client) NotifyOnMessageID(chatID string, messageID []byte) error { - return c.persistence.TrackPushNotification(chatID, messageID) +// handleMessageScheduled keeps track of the message to make sure we notify on it +func (c *Client) handleMessageScheduled(message *common.RawMessage) error { + if !message.SendPushNotification { + return nil + } + messageID, err := types.DecodeHex(message.ID) + if err != nil { + return err + } + return c.persistence.TrackPushNotification(message.LocalChatID, messageID) } +// shouldNotifyOn check whether we should notify a particular public-key/installation-id/message-id combination func (c *Client) shouldNotifyOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) { if len(installationID) == 0 { return c.persistence.ShouldSendNotificationToAllInstallationIDs(publicKey, messageID) - } else { - return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID) } + return c.persistence.ShouldSendNotificationFor(publicKey, installationID, messageID) } +// notifiedOn marks a combination of publickey/installationid/messageID as notified func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) error { - c.config.Logger.Info("notified on") return c.persistence.UpsertSentNotification(&SentNotification{ PublicKey: publicKey, LastTriedAt: time.Now().Unix(), @@ -364,7 +741,8 @@ func (c *Client) notifiedOn(publicKey *ecdsa.PublicKey, installationID string, m MessageID: messageID, }) } -func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { + +func (c *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { var mutedChatListHashes [][]byte for _, chatID := range chatIDs { @@ -374,8 +752,8 @@ func (p *Client) mutedChatIDsHashes(chatIDs []string) [][]byte { return mutedChatListHashes } -func (p *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { - sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( +func (c *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { + sharedKey, err := ecies.ImportECDSA(c.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), accessTokenKeyLength, accessTokenKeyLength, @@ -383,15 +761,15 @@ func (p *Client) encryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, if err != nil { return nil, err } - encryptedToken, err := encryptAccessToken(token, sharedKey, p.reader) + encryptedToken, err := encryptAccessToken(token, sharedKey, c.reader) if err != nil { return nil, err } return encryptedToken, nil } -func (p *Client) decryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { - sharedKey, err := ecies.ImportECDSA(p.config.Identity).GenerateShared( +func (c *Client) decryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, error) { + sharedKey, err := ecies.ImportECDSA(c.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), accessTokenKeyLength, accessTokenKeyLength, @@ -406,9 +784,10 @@ func (p *Client) decryptToken(publicKey *ecdsa.PublicKey, token []byte) ([]byte, return decryptedToken, nil } -func (c *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([][]byte, error) { +// allowedKeyList builds up a list of encrypted tokens, used for registering with the server +func (c *Client) allowedKeyList(token []byte, contactIDs []*ecdsa.PublicKey) ([][]byte, error) { // If we allow everyone, don't set the list - if !c.config.allowFromContactsOnly { + if !c.config.AllowFromContactsOnly { return nil, nil } var encryptedTokens [][]byte @@ -425,15 +804,16 @@ func (c *Client) allowedUserList(token []byte, contactIDs []*ecdsa.PublicKey) ([ } // getToken checks if we need to refresh the token -// and return a new one in that case +// and return a new one in that case. A token is refreshed only if it's not set +// or if a contact has been removed func (c *Client) getToken(contactIDs []*ecdsa.PublicKey) string { if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs) { c.config.Logger.Info("refreshing access token") return uuid.New().String() } return c.lastPushNotificationRegistration.AccessToken - } + func (c *Client) getVersion() uint64 { if c.lastPushNotificationRegistration == nil { return 1 @@ -443,7 +823,7 @@ func (c *Client) getVersion() uint64 { func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) (*protobuf.PushNotificationRegistration, error) { token := c.getToken(contactIDs) - allowedUserList, err := c.allowedUserList([]byte(token), contactIDs) + allowedKeyList, err := c.allowedKeyList([]byte(token), contactIDs) if err != nil { return nil, err } @@ -453,11 +833,11 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu TokenType: c.config.TokenType, Version: c.getVersion(), InstallationId: c.config.InstallationID, - Token: c.deviceToken, - AllowFromContactsOnly: c.config.allowFromContactsOnly, + DeviceToken: c.deviceToken, + AllowFromContactsOnly: c.config.AllowFromContactsOnly, Enabled: c.config.RemoteNotificationsEnabled, BlockedChatList: c.mutedChatIDsHashes(mutedChatIDs), - AllowedUserList: allowedUserList, + AllowedKeyList: allowedKeyList, } return options, nil } @@ -530,18 +910,22 @@ func (c *Client) resetServers() error { return nil } +// registerWithServer will register with a push notification server. This will use +// the user identity key for dispatching, as the content is in any case signed, so identity needs to be revealed. func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegistration, server *PushNotificationServer) error { - // Reset server registration data + // reset server registration data server.Registered = false server.RegisteredAt = 0 - server.RetryCount += 1 + server.RetryCount++ server.LastRetriedAt = time.Now().Unix() server.AccessToken = registration.AccessToken + // save if err := c.persistence.UpsertServer(server); err != nil { return err } + // build grant for this specific server grant, err := c.buildGrantSignature(server.PublicKey, registration.AccessToken) if err != nil { c.config.Logger.Error("failed to build grant", zap.Error(err)) @@ -550,12 +934,13 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis registration.Grant = grant + // marshal message marshaledRegistration, err := proto.Marshal(registration) if err != nil { return err } - // Dispatch message + // encrypt and dispatch message encryptedRegistration, err := c.encryptRegistration(server.PublicKey, marshaledRegistration) if err != nil { return err @@ -573,36 +958,39 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis return nil } +// sendNotification sends an actual notification to the push notification server. +// the notification is sent using an ephemeral key to shield the real identity of the sender func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []string, messageID []byte) ([]*PushNotificationInfo, error) { + // get latest push notification infos err := c.queryNotificationInfo(publicKey, false) if err != nil { return nil, err } - c.config.Logger.Info("queried info") - // Retrieve infos + c.config.Logger.Debug("queried info") + + // retrieve info from the database info, err := c.GetPushNotificationInfo(publicKey, installationIDs) if err != nil { c.config.Logger.Error("could not get pn info", zap.Error(err)) return nil, err } - // Naively dispatch to the first server for now - // This wait for an acknowledgement and try a different server after a timeout - // Also we sent a single notification for multiple message ids, need to check with UI what's the desired behavior + // naively dispatch to the first server for now + // push notifications are only retried for now if a WRONG_TOKEN response is returned. + // we should also retry if no response at all is received after a timeout. + // also we send a single notification for multiple message ids, need to check with UI what's the desired behavior - // Sort by server so we tend to hit the same one + // sort by server so we tend to hit the same one sort.Slice(info, func(i, j int) bool { return info[i].ServerPublicKey.X.Cmp(info[j].ServerPublicKey.X) <= 0 }) - c.config.Logger.Info("retrieved info") - installationIDsMap := make(map[string]bool) - // One info per installation id, grouped by server + + // one info per installation id, grouped by server actionableInfos := make(map[string][]*PushNotificationInfo) for _, i := range info { - c.config.Logger.Info("queried info", zap.String("id", i.InstallationID)) if !installationIDsMap[i.InstallationID] { serverKey := hex.EncodeToString(crypto.CompressPubkey(i.ServerPublicKey)) actionableInfos[serverKey] = append(actionableInfos[serverKey], i) @@ -611,12 +999,14 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] } - c.config.Logger.Info("actionable info", zap.Int("count", len(actionableInfos))) + c.config.Logger.Debug("actionable info", zap.Int("count", len(actionableInfos))) + + // add ephemeral key and listen to it ephemeralKey, err := crypto.GenerateKey() if err != nil { return nil, err } - _, err = c.messageProcessor.LoadKeyFilters(ephemeralKey) + _, err = c.messageProcessor.AddEphemeralKey(ephemeralKey) if err != nil { return nil, err } @@ -645,13 +1035,14 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] } rawMessage := &common.RawMessage{ - Payload: payload, - Sender: ephemeralKey, - SkipNegotiation: true, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, + Payload: payload, + Sender: ephemeralKey, + // we skip encryption as we don't want to save any key material + // for an ephemeral key, no need to use pfs as these are throw away keys + SkipEncryption: true, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, } - // TODO: We should use the messageID for the response _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, rawMessage) if err != nil { @@ -663,18 +1054,16 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] } func (c *Client) resendNotification(pn *SentNotification) error { - c.config.Logger.Info("resending notification", zap.Any("notification", pn)) - pn.RetryCount += 1 + c.config.Logger.Debug("resending notification") + pn.RetryCount++ pn.LastTriedAt = time.Now().Unix() - c.config.Logger.Info("PN", zap.Any("pn", pn)) err := c.persistence.UpsertSentNotification(pn) if err != nil { c.config.Logger.Error("failed to upsert notification", zap.Error(err)) return err } - c.config.Logger.Info("GOING INTO querying") - // Re-fetch push notification info + // re-fetch push notification info err = c.queryNotificationInfo(pn.PublicKey, true) if err != nil { c.config.Logger.Error("failed to query notification info", zap.Error(err)) @@ -690,11 +1079,13 @@ func (c *Client) resendNotification(pn *SentNotification) error { return err } +// resendingLoop is a loop that is running when push notifications need to be resent, it only runs when needed, it will quit if no work is necessary. func (c *Client) resendingLoop() error { for { - c.config.Logger.Info("running resending loop") + c.config.Logger.Debug("running resending loop") var lowestNextRetry int64 + // fetch retriable notifications retriableNotifications, err := c.persistence.GetRetriablePushNotifications() if err != nil { c.config.Logger.Error("failed retrieving notifications, quitting resending loop", zap.Error(err)) @@ -707,38 +1098,38 @@ func (c *Client) resendingLoop() error { } for _, pn := range retriableNotifications { - nR := nextPushNotificationRetry(pn) - c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nR)) + + // check if we should retry the notification if shouldRetryPushNotification(pn) { - c.config.Logger.Info("retrying pn", zap.Any("pn", pn)) + c.config.Logger.Debug("retrying pn") err := c.resendNotification(pn) if err != nil { return err } } + // set the lowest next retry if necessary nextRetry := nextPushNotificationRetry(pn) - c.config.Logger.Info("Next next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nextRetry), zap.Any("pn", pn)) if lowestNextRetry == 0 || nextRetry < lowestNextRetry { lowestNextRetry = nextRetry } } nextRetry := lowestNextRetry - time.Now().Unix() + // how long should we sleep for? waitFor := time.Duration(nextRetry) select { case <-time.After(waitFor * time.Second): case <-c.resendingLoopQuitChan: return nil - } - } } +// registrationLoop is a loop that is running when we need to register with a push notification server, it only runs when needed, it will quit if no work is necessary. func (c *Client) registrationLoop() error { for { - c.config.Logger.Info("running registration loop") + c.config.Logger.Debug("running registration loop") servers, err := c.persistence.GetServers() if err != nil { c.config.Logger.Error("failed retrieving servers, quitting registration loop", zap.Error(err)) @@ -755,20 +1146,19 @@ func (c *Client) registrationLoop() error { nonRegisteredServers = append(nonRegisteredServers, server) } } + if len(nonRegisteredServers) == 0 { c.config.Logger.Debug("registered with all servers, quitting registration loop") return nil } - c.config.Logger.Info("Trying to register with", zap.Int("servers", len(nonRegisteredServers))) + c.config.Logger.Debug("Trying to register with", zap.Int("servers", len(nonRegisteredServers))) var lowestNextRetry int64 for _, server := range nonRegisteredServers { - nR := nextServerRetry(server) - c.config.Logger.Info("Next retry", zap.Int64("now", time.Now().Unix()), zap.Int64("next", nR)) if shouldRetryRegisteringWithServer(server) { - c.config.Logger.Info("registering with server", zap.Any("server", server)) + c.config.Logger.Debug("registering with server", zap.Any("server", server)) err := c.registerWithServer(c.lastPushNotificationRegistration, server) if err != nil { return err @@ -778,43 +1168,21 @@ func (c *Client) registrationLoop() error { if lowestNextRetry == 0 || nextRetry < lowestNextRetry { lowestNextRetry = nextRetry } - } nextRetry := lowestNextRetry - time.Now().Unix() waitFor := time.Duration(nextRetry) - c.config.Logger.Info("Waiting for", zap.Any("wait for", waitFor)) + c.config.Logger.Debug("Waiting for", zap.Any("wait for", waitFor)) select { case <-time.After(waitFor * time.Second): case <-c.registrationLoopQuitChan: return nil - } } } -func (c *Client) Unregister() error { - // stop registration loop - c.stopRegistrationLoop() - - registration := c.buildPushNotificationUnregisterMessage() - err := c.SaveLastPushNotificationRegistration(registration, nil) - if err != nil { - return err - } - // Reset servers - err = c.resetServers() - if err != nil { - return err - } - - // and asynchronously register - c.startRegistrationLoop() - return nil -} - -func (c *Client) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error { +func (c *Client) saveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error { // stop registration loop c.stopRegistrationLoop() @@ -829,96 +1197,6 @@ func (c *Client) SaveLastPushNotificationRegistration(registration *protobuf.Pus return nil } -func (c *Client) Registered() (bool, error) { - servers, err := c.persistence.GetServers() - if err != nil { - return false, err - } - - for _, s := range servers { - if !s.Registered { - return false, nil - } - } - - return true, nil -} - -func (c *Client) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) { - return c.persistence.GetSentNotification(hashedPublicKey, installationID, messageID) -} - -func (c *Client) GetServers() ([]*PushNotificationServer, error) { - return c.persistence.GetServers() -} - -func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { - c.config.Logger.Info("re-registering") - if len(c.deviceToken) == 0 { - c.config.Logger.Info("no device token, not registering") - return nil - } - - return c.Register(c.deviceToken, contactIDs, mutedChatIDs) -} - -func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { - // stop registration loop - c.stopRegistrationLoop() - - // Reset servers - err := c.resetServers() - if err != nil { - return err - } - - c.deviceToken = deviceToken - - registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs) - if err != nil { - return err - } - - err = c.SaveLastPushNotificationRegistration(registration, contactIDs) - if err != nil { - return err - } - - c.startRegistrationLoop() - - return nil -} - -// HandlePushNotificationRegistrationResponse should check whether the response was successful or not, retry if necessary otherwise store the result in the database -func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, response protobuf.PushNotificationRegistrationResponse) error { - c.config.Logger.Info("received push notification registration response", zap.Any("response", response)) - // TODO: handle non successful response and match request id - // Not successful ignore for now - if !response.Success { - return errors.New("response was not successful") - } - - servers, err := c.persistence.GetServersByPublicKey([]*ecdsa.PublicKey{publicKey}) - if err != nil { - return err - } - // We haven't registered with this server - if len(servers) != 1 { - return errors.New("not registered with this server, ignoring") - } - - server := servers[0] - server.Registered = true - server.RegisteredAt = time.Now().Unix() - - return c.persistence.UpsertServer(server) -} - -// HandlePushNotificationAdvertisement should store any info related to push notifications -func (p *Client) HandlePushNotificationAdvertisement(info *protobuf.PushNotificationAdvertisementInfo) error { - return nil -} - // buildGrantSignatureMaterial builds a grant for a specific server. // We use 3 components: // 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid @@ -952,132 +1230,24 @@ func (c *Client) handleGrant(clientPublicKey *ecdsa.PublicKey, serverPublicKey * return nil } -func (c *Client) handleAllowedUserList(publicKey *ecdsa.PublicKey, allowedUserList [][]byte) string { - c.config.Logger.Info("handling allowed user list") - for _, encryptedToken := range allowedUserList { +// handleAllowedKeyList will try to decrypt a token from the list, to see if we are allowed to send push notification to a given user +func (c *Client) handleAllowedKeyList(publicKey *ecdsa.PublicKey, allowedKeyList [][]byte) string { + c.config.Logger.Debug("handling allowed key list") + for _, encryptedToken := range allowedKeyList { token, err := c.decryptToken(publicKey, encryptedToken) if err != nil { c.config.Logger.Warn("could not decrypt token", zap.Error(err)) continue } - c.config.Logger.Info("decrypted token") + c.config.Logger.Debug("decrypted token") return string(token) } return "" } -// HandlePushNotificationQueryResponse should update the data in the database for a given user -func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.PublicKey, response protobuf.PushNotificationQueryResponse) error { - - c.config.Logger.Info("received push notification query response", zap.Any("response", response)) - if len(response.Info) == 0 { - return errors.New("empty response from the server") - } - - publicKey, err := c.persistence.GetQueryPublicKey(response.MessageId) - if err != nil { - return err - } - if publicKey == nil { - c.config.Logger.Info("query not found") - return nil - } - var pushNotificationInfo []*PushNotificationInfo - for _, info := range response.Info { - if bytes.Compare(info.PublicKey, common.HashPublicKey(publicKey)) != 0 { - c.config.Logger.Warn("reply for different key, ignoring") - continue - } - - accessToken := info.AccessToken - - if len(accessToken) == 0 && len(info.AllowedUserList) != 0 { - accessToken = c.handleAllowedUserList(publicKey, info.AllowedUserList) - - } - - if len(accessToken) == 0 { - c.config.Logger.Info("not in the allowed users list") - continue - } - - // We check the user has allowed this server to store this particular - // access token, otherwise anyone could reply with a fake token - // and receive notifications for a user - if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, accessToken); err != nil { - c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err)) - continue - } - pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{ - PublicKey: publicKey, - ServerPublicKey: serverPublicKey, - AccessToken: accessToken, - InstallationID: info.InstallationId, - Version: info.Version, - RetrievedAt: time.Now().Unix(), - }) - - } - err = c.persistence.SavePushNotificationInfo(pushNotificationInfo) - if err != nil { - c.config.Logger.Error("failed to save push notifications", zap.Error(err)) - return err - } - - return nil -} - -// HandlePushNotificationResponse should set the request as processed -func (c *Client) HandlePushNotificationResponse(serverKey *ecdsa.PublicKey, response protobuf.PushNotificationResponse) error { - messageID := response.MessageId - c.config.Logger.Info("received response for", zap.Binary("message-id", messageID)) - for _, report := range response.Reports { - c.config.Logger.Info("received response", zap.Any("report", report)) - err := c.persistence.UpdateNotificationResponse(messageID, report) - if err != nil { - return err - } - } - // Restart resending loop - c.stopResendingLoop() - c.startResendingLoop() - return nil -} - -func (c *Client) RemovePushNotificationServer(publicKey *ecdsa.PublicKey) error { - c.config.Logger.Info("removing push notification server", zap.Any("public-key", publicKey)) - //TODO: this needs implementing. It requires unregistering from the server and - // likely invalidate the device token of the user - return errors.New("not implemented") -} - -func (c *Client) AddPushNotificationServer(publicKey *ecdsa.PublicKey) error { - c.config.Logger.Info("adding push notification server", zap.Any("public-key", publicKey)) - currentServers, err := c.persistence.GetServers() - if err != nil { - return err - } - - for _, server := range currentServers { - if common.IsPubKeyEqual(server.PublicKey, publicKey) { - return errors.New("push notification server already added") - } - } - - err = c.persistence.UpsertServer(&PushNotificationServer{ - PublicKey: publicKey, - }) - if err != nil { - return err - } - - if c.config.RemoteNotificationsEnabled { - c.startRegistrationLoop() - } - return nil -} - -func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { +// queryPushNotificationInfo sends a message to any server who has the given user registered. +// it uses an ephemeral key so the identity of the client querying is not disclosed +func (c *Client) queryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { hashedPublicKey := common.HashPublicKey(publicKey) query := &protobuf.PushNotificationQuery{ PublicKeys: [][]byte{hashedPublicKey}, @@ -1093,20 +1263,20 @@ func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { } rawMessage := &common.RawMessage{ - Payload: encodedMessage, - Sender: ephemeralKey, - SkipNegotiation: true, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY, + Payload: encodedMessage, + Sender: ephemeralKey, + // we don't want to wrap in an encryption layer message + SkipEncryption: true, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY, } - filter, err := c.messageProcessor.LoadKeyFilters(ephemeralKey) + _, err = c.messageProcessor.AddEphemeralKey(ephemeralKey) if err != nil { return err } - c.config.Logger.Debug("Filter", zap.String("filter-id", filter.FilterID)) + // this is the topic of message encodedPublicKey := hex.EncodeToString(hashedPublicKey) - c.config.Logger.Info("sending query") messageID, err := c.messageProcessor.SendPublic(context.Background(), encodedPublicKey, rawMessage) if err != nil { @@ -1115,76 +1285,3 @@ func (c *Client) QueryPushNotificationInfo(publicKey *ecdsa.PublicKey) error { return c.persistence.SavePushNotificationQuery(publicKey, messageID) } - -func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) { - if len(installationIDs) == 0 { - return c.persistence.GetPushNotificationInfoByPublicKey(publicKey) - } else { - return c.persistence.GetPushNotificationInfo(publicKey, installationIDs) - } -} - -func (c *Client) EnableSending() { - c.config.SendEnabled = true -} - -func (c *Client) DisableSending() { - c.config.SendEnabled = false -} - -func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { - c.config.allowFromContactsOnly = true - if c.lastPushNotificationRegistration != nil { - return c.Register(c.deviceToken, contactIDs, mutedChatIDs) - } - return nil -} - -func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error { - c.config.allowFromContactsOnly = false - if c.lastPushNotificationRegistration != nil { - return c.Register(c.deviceToken, contactIDs, mutedChatIDs) - } - return nil -} - -func (c *Client) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { - encodedPublicKey := hex.EncodeToString(hashedPublicKey) - return c.messageProcessor.JoinPublic(encodedPublicKey) -} - -func encryptAccessToken(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) { - c, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(c) - if err != nil { - return nil, err - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err = io.ReadFull(reader, nonce); err != nil { - return nil, err - } - - return gcm.Seal(nonce, nonce, plaintext, nil), nil -} - -func (c *Client) encryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { - sharedKey, err := c.generateSharedKey(publicKey) - if err != nil { - return nil, err - } - - return common.Encrypt(payload, sharedKey, c.reader) -} - -func (c *Client) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { - return ecies.ImportECDSA(c.config.Identity).GenerateShared( - ecies.ImportECDSAPublic(publicKey), - encryptedPayloadKeyLength, - encryptedPayloadKeyLength, - ) -} diff --git a/protocol/push_notification_client/client_test.go b/protocol/pushnotificationclient/client_test.go similarity index 90% rename from protocol/push_notification_client/client_test.go rename to protocol/pushnotificationclient/client_test.go index 11b5aaa03..1c372fdbe 100644 --- a/protocol/push_notification_client/client_test.go +++ b/protocol/pushnotificationclient/client_test.go @@ -1,4 +1,4 @@ -package push_notification_client +package pushnotificationclient import ( "bytes" @@ -6,19 +6,22 @@ import ( "io/ioutil" "math/rand" "os" - "testing" "github.com/google/uuid" + "github.com/stretchr/testify/suite" + "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/sqlite" "github.com/status-im/status-go/protocol/tt" - "github.com/stretchr/testify/suite" ) +const testDeviceToken = "test-token" + type ClientSuite struct { suite.Suite tmpFile *os.File @@ -59,7 +62,6 @@ func (s *ClientSuite) SetupTest() { } func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { - myDeviceToken := "device-token" mutedChatList := []string{"a", "b"} // build chat lish hashes @@ -82,14 +84,14 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - s.client.deviceToken = myDeviceToken + s.client.deviceToken = testDeviceToken // Set reader s.client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationRegistration{ Version: 1, AccessToken: expectedUUID, - Token: myDeviceToken, + DeviceToken: testDeviceToken, InstallationId: s.installationID, Enabled: true, BlockedChatList: mutedChatListHashes, @@ -102,7 +104,6 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() { } func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() { - myDeviceToken := "device-token" mutedChatList := []string{"a", "b"} // build chat lish hashes @@ -138,20 +139,20 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsO // Reset random generator uuid.SetRand(rand.New(rand.NewSource(seed))) - s.client.config.allowFromContactsOnly = true - s.client.deviceToken = myDeviceToken + s.client.config.AllowFromContactsOnly = true + s.client.deviceToken = testDeviceToken // Set reader s.client.reader = bytes.NewReader([]byte(expectedUUID)) options := &protobuf.PushNotificationRegistration{ Version: 1, AccessToken: expectedUUID, - Token: myDeviceToken, + DeviceToken: testDeviceToken, InstallationId: s.installationID, AllowFromContactsOnly: true, Enabled: true, BlockedChatList: mutedChatListHashes, - AllowedUserList: [][]byte{encryptedToken}, + AllowedKeyList: [][]byte{encryptedToken}, } actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList) @@ -160,13 +161,18 @@ func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsO s.Require().Equal(options, actualMessage) } -func (s *ClientSuite) TestNotifyOnMessageID() { +func (s *ClientSuite) TestHandleMessageScheduled() { messageID := []byte("message-id") chatID := "chat-id" installationID1 := "1" installationID2 := "2" + rawMessage := &common.RawMessage{ + ID: types.EncodeHex(messageID), + SendPushNotification: true, + LocalChatID: chatID, + } - s.Require().NoError(s.client.NotifyOnMessageID(chatID, messageID)) + s.Require().NoError(s.client.handleMessageScheduled(rawMessage)) key1, err := crypto.GenerateKey() s.Require().NoError(err) diff --git a/protocol/push_notification_client/migrations/migrations.go b/protocol/pushnotificationclient/migrations/migrations.go similarity index 98% rename from protocol/push_notification_client/migrations/migrations.go rename to protocol/pushnotificationclient/migrations/migrations.go index 61cd5e54d..e6d7b022b 100644 --- a/protocol/push_notification_client/migrations/migrations.go +++ b/protocol/pushnotificationclient/migrations/migrations.go @@ -86,7 +86,7 @@ func _1593601729_initial_schemaDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} + info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa, 0x95, 0x55, 0x64, 0x38, 0x40, 0x16, 0xbf, 0x8b, 0x1c, 0x18, 0xb4, 0xc5, 0x7f, 0xd0, 0xb8, 0xf0, 0x3c, 0xa2, 0x82, 0xf8, 0x8d, 0x5a, 0xd3, 0xb6, 0x6e, 0xa3, 0xb4, 0xc, 0x9, 0x33, 0x0}} return a, nil } @@ -106,7 +106,7 @@ func _1593601729_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1773, mode: os.FileMode(0644), modTime: time.Unix(1595256491, 0)} + info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1773, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x1e, 0x5, 0x35, 0x9, 0xb2, 0x2d, 0x6f, 0x33, 0x63, 0xa2, 0x7a, 0x5b, 0xd2, 0x2d, 0xcb, 0x79, 0x7e, 0x6, 0xb4, 0x9d, 0x35, 0xd8, 0x9b, 0x55, 0xe5, 0xf8, 0x44, 0xca, 0xa6, 0xf3, 0xd3}} return a, nil } @@ -126,7 +126,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} return a, nil } diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.down.sql b/protocol/pushnotificationclient/migrations/sql/1593601729_initial_schema.down.sql similarity index 100% rename from protocol/push_notification_client/migrations/sql/1593601729_initial_schema.down.sql rename to protocol/pushnotificationclient/migrations/sql/1593601729_initial_schema.down.sql diff --git a/protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql b/protocol/pushnotificationclient/migrations/sql/1593601729_initial_schema.up.sql similarity index 100% rename from protocol/push_notification_client/migrations/sql/1593601729_initial_schema.up.sql rename to protocol/pushnotificationclient/migrations/sql/1593601729_initial_schema.up.sql diff --git a/protocol/push_notification_client/migrations/sql/doc.go b/protocol/pushnotificationclient/migrations/sql/doc.go similarity index 100% rename from protocol/push_notification_client/migrations/sql/doc.go rename to protocol/pushnotificationclient/migrations/sql/doc.go diff --git a/protocol/push_notification_client/persistence.go b/protocol/pushnotificationclient/persistence.go similarity index 98% rename from protocol/push_notification_client/persistence.go rename to protocol/pushnotificationclient/persistence.go index bc657be94..5a49639f0 100644 --- a/protocol/push_notification_client/persistence.go +++ b/protocol/pushnotificationclient/persistence.go @@ -1,4 +1,4 @@ -package push_notification_client +package pushnotificationclient import ( "bytes" @@ -180,10 +180,13 @@ func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, instal inVector := strings.Repeat("?, ", len(installationIDs)-1) + "?" - rows, err := p.db.Query(`SELECT server_public_key, installation_id, version, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) + rows, err := p.db.Query(`SELECT server_public_key, installation_id, version, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) //nolint: gosec + if err != nil { return nil, err } + defer rows.Close() + var infos []*PushNotificationInfo for rows.Next() { var serverPublicKeyBytes []byte @@ -210,6 +213,8 @@ func (p *Persistence) GetPushNotificationInfoByPublicKey(publicKey *ecdsa.Public if err != nil { return nil, err } + defer rows.Close() + var infos []*PushNotificationInfo for rows.Next() { var serverPublicKeyBytes []byte @@ -308,6 +313,7 @@ func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, erro if err != nil { return nil, err } + defer rows.Close() for rows.Next() { var publicKeyBytes []byte @@ -337,6 +343,8 @@ func (p *Persistence) GetServers() ([]*PushNotificationServer, error) { if err != nil { return nil, err } + defer rows.Close() + var servers []*PushNotificationServer for rows.Next() { server := &PushNotificationServer{} @@ -367,6 +375,8 @@ func (p *Persistence) GetServersByPublicKey(keys []*ecdsa.PublicKey) ([]*PushNot if err != nil { return nil, err } + defer rows.Close() + var servers []*PushNotificationServer for rows.Next() { server := &PushNotificationServer{} diff --git a/protocol/push_notification_client/persistence_test.go b/protocol/pushnotificationclient/persistence_test.go similarity index 94% rename from protocol/push_notification_client/persistence_test.go rename to protocol/pushnotificationclient/persistence_test.go index 8f522c767..cdb401b43 100644 --- a/protocol/push_notification_client/persistence_test.go +++ b/protocol/pushnotificationclient/persistence_test.go @@ -1,4 +1,4 @@ -package push_notification_client +package pushnotificationclient import ( "crypto/ecdsa" @@ -16,6 +16,13 @@ import ( "github.com/status-im/status-go/protocol/sqlite" ) +const ( + testAccessToken = "token" + installationID1 = "installation-id-1" + installationID2 = "installation-id-2" + installationID3 = "installation-id-3" +) + func TestSQLitePersistenceSuite(t *testing.T) { suite.Run(t, new(SQLitePersistenceSuite)) } @@ -43,13 +50,12 @@ func (s *SQLitePersistenceSuite) TearDownTest() { func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { key, err := crypto.GenerateKey() s.Require().NoError(err) - accessToken := "token" server := &PushNotificationServer{ PublicKey: &key.PublicKey, Registered: true, RegisteredAt: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, } s.Require().NoError(s.persistence.UpsertServer(server)) @@ -61,7 +67,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { s.Require().True(retrievedServers[0].Registered) s.Require().Equal(int64(1), retrievedServers[0].RegisteredAt) s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey)) - s.Require().Equal(accessToken, retrievedServers[0].AccessToken) + s.Require().Equal(testAccessToken, retrievedServers[0].AccessToken) server.Registered = false server.RegisteredAt = 2 @@ -78,9 +84,6 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() { } func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { - installationID1 := "installation-id-1" - installationID2 := "installation-id-2" - installationID3 := "installation-id-3" key1, err := crypto.GenerateKey() s.Require().NoError(err) key2, err := crypto.GenerateKey() @@ -88,15 +91,13 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { serverKey, err := crypto.GenerateKey() s.Require().NoError(err) - accessToken := "token" - infos := []*PushNotificationInfo{ { PublicKey: &key1.PublicKey, ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID1, }, { @@ -104,7 +105,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID2, }, { @@ -112,7 +113,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID3, }, { @@ -120,7 +121,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID1, }, { @@ -128,7 +129,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID2, }, { @@ -136,7 +137,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() { ServerPublicKey: &serverKey.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID3, }, } @@ -158,15 +159,13 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() { serverKey2, err := crypto.GenerateKey() s.Require().NoError(err) - accessToken := "token" - infos := []*PushNotificationInfo{ { PublicKey: &key.PublicKey, ServerPublicKey: &serverKey1.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID, }, { @@ -174,7 +173,7 @@ func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() { ServerPublicKey: &serverKey2.PublicKey, RetrievedAt: 1, Version: 1, - AccessToken: accessToken, + AccessToken: testAccessToken, InstallationID: installationID, }, } diff --git a/protocol/push_notification_server/errors.go b/protocol/pushnotificationserver/errors.go similarity index 96% rename from protocol/push_notification_server/errors.go rename to protocol/pushnotificationserver/errors.go index 2199fa91b..ea9a580b3 100644 --- a/protocol/push_notification_server/errors.go +++ b/protocol/pushnotificationserver/errors.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import "errors" diff --git a/protocol/push_notification_server/gorush.go b/protocol/pushnotificationserver/gorush.go similarity index 96% rename from protocol/push_notification_server/gorush.go rename to protocol/pushnotificationserver/gorush.go index ff88a0205..e791583b0 100644 --- a/protocol/push_notification_server/gorush.go +++ b/protocol/pushnotificationserver/gorush.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "bytes" @@ -46,12 +46,11 @@ func tokenTypeToGoRushPlatform(tokenType protobuf.PushNotificationRegistration_T func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*RequestAndRegistration) *GoRushRequest { goRushRequests := &GoRushRequest{} for _, requestAndRegistration := range requestAndRegistrations { - request := requestAndRegistration.Request registration := requestAndRegistration.Registration goRushRequests.Notifications = append(goRushRequests.Notifications, &GoRushRequestNotification{ - Tokens: []string{registration.Token}, + Tokens: []string{registration.DeviceToken}, Platform: tokenTypeToGoRushPlatform(registration.TokenType), Message: defaultNotificationMessage, Data: &GoRushRequestData{ diff --git a/protocol/push_notification_server/gorush_test.go b/protocol/pushnotificationserver/gorush_test.go similarity index 89% rename from protocol/push_notification_server/gorush_test.go rename to protocol/pushnotificationserver/gorush_test.go index fda1de8a5..f60b21a01 100644 --- a/protocol/push_notification_server/gorush_test.go +++ b/protocol/pushnotificationserver/gorush_test.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "encoding/hex" @@ -38,8 +38,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: message1, }, Registration: &protobuf.PushNotificationRegistration{ - Token: token1, - TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + DeviceToken: token1, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, }, }, { @@ -50,8 +50,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: message2, }, Registration: &protobuf.PushNotificationRegistration{ - Token: token2, - TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, + DeviceToken: token2, + TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, }, }, { @@ -62,8 +62,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: message3, }, Registration: &protobuf.PushNotificationRegistration{ - Token: token3, - TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, + DeviceToken: token3, + TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN, }, }, } diff --git a/protocol/push_notification_server/migrations/migrations.go b/protocol/pushnotificationserver/migrations/migrations.go similarity index 98% rename from protocol/push_notification_server/migrations/migrations.go rename to protocol/pushnotificationserver/migrations/migrations.go index c044d1bf6..a663903c9 100644 --- a/protocol/push_notification_server/migrations/migrations.go +++ b/protocol/pushnotificationserver/migrations/migrations.go @@ -86,7 +86,7 @@ func _1593601728_initial_schemaDownSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}} return a, nil } @@ -106,7 +106,7 @@ func _1593601728_initial_schemaUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1594896582, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}} return a, nil } @@ -126,7 +126,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1594896579, 0)} + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}} return a, nil } diff --git a/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.down.sql b/protocol/pushnotificationserver/migrations/sql/1593601728_initial_schema.down.sql similarity index 100% rename from protocol/push_notification_server/migrations/sql/1593601728_initial_schema.down.sql rename to protocol/pushnotificationserver/migrations/sql/1593601728_initial_schema.down.sql diff --git a/protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql b/protocol/pushnotificationserver/migrations/sql/1593601728_initial_schema.up.sql similarity index 100% rename from protocol/push_notification_server/migrations/sql/1593601728_initial_schema.up.sql rename to protocol/pushnotificationserver/migrations/sql/1593601728_initial_schema.up.sql diff --git a/protocol/push_notification_server/migrations/sql/doc.go b/protocol/pushnotificationserver/migrations/sql/doc.go similarity index 100% rename from protocol/push_notification_server/migrations/sql/doc.go rename to protocol/pushnotificationserver/migrations/sql/doc.go diff --git a/protocol/push_notification_server/persistence.go b/protocol/pushnotificationserver/persistence.go similarity index 98% rename from protocol/push_notification_server/persistence.go rename to protocol/pushnotificationserver/persistence.go index a4d039a0b..0f31fb178 100644 --- a/protocol/push_notification_server/persistence.go +++ b/protocol/pushnotificationserver/persistence.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "crypto/ecdsa" @@ -70,7 +70,7 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe inVector := strings.Repeat("?, ", len(publicKeys)-1) + "?" - rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE public_key IN (`+inVector+`)`, publicKeyArgs...) + rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec if err != nil { return nil, err } diff --git a/protocol/push_notification_server/persistence_test.go b/protocol/pushnotificationserver/persistence_test.go similarity index 96% rename from protocol/push_notification_server/persistence_test.go rename to protocol/pushnotificationserver/persistence_test.go index e81204cab..31029f99d 100644 --- a/protocol/push_notification_server/persistence_test.go +++ b/protocol/pushnotificationserver/persistence_test.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "io/ioutil" @@ -14,8 +14,6 @@ import ( "github.com/status-im/status-go/protocol/sqlite" ) -//tmpFile, err := ioutil.TempFile("", "") - func TestSQLitePersistenceSuite(t *testing.T) { suite.Run(t, new(SQLitePersistenceSuite)) } diff --git a/protocol/push_notification_server/server.go b/protocol/pushnotificationserver/server.go similarity index 76% rename from protocol/push_notification_server/server.go rename to protocol/pushnotificationserver/server.go index bd84511d6..03bacca00 100644 --- a/protocol/push_notification_server/server.go +++ b/protocol/pushnotificationserver/server.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "context" @@ -8,13 +8,13 @@ import ( "github.com/golang/protobuf/proto" "github.com/google/uuid" + "go.uber.org/zap" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto/ecies" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" - "go.uber.org/zap" ) const encryptedPayloadKeyLength = 16 @@ -43,15 +43,150 @@ func New(config *Config, persistence Persistence, messageProcessor *common.Messa return &Server{persistence: persistence, config: config, messageProcessor: messageProcessor} } -func (p *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { - return ecies.ImportECDSA(p.config.Identity).GenerateShared( +func (s *Server) Start() error { + s.config.Logger.Info("starting push notification server") + if s.config.Identity == nil { + s.config.Logger.Info("Identity nil") + // Pull identity from database + identity, err := s.persistence.GetIdentity() + if err != nil { + return err + } + if identity == nil { + identity, err = crypto.GenerateKey() + if err != nil { + return err + } + if err := s.persistence.SaveIdentity(identity); err != nil { + return err + } + } + s.config.Identity = identity + } + + pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys() + if err != nil { + return err + } + // listen to all topics for users registered + for _, pk := range pks { + if err := s.listenToPublicKeyQueryTopic(pk); err != nil { + return err + } + } + + s.config.Logger.Info("started push notification server", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&s.config.Identity.PublicKey)))) + + return nil +} + +// HandlePushNotificationRegistration builds a response for the registration and sends it back to the user +func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) error { + response := s.buildPushNotificationRegistrationResponse(publicKey, payload) + if response == nil { + return nil + } + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE, + // we skip encryption as might be sent from an ephemeral key + SkipEncryption: true, + } + + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err +} + +// HandlePushNotificationQuery builds a response for the query and sends it back to the user +func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, messageID []byte, query protobuf.PushNotificationQuery) error { + response := s.buildPushNotificationQueryResponse(&query) + if response == nil { + return nil + } + response.MessageId = messageID + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE, + // we skip encryption as sent from an ephemeral key + SkipEncryption: true, + } + + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err +} + +// HandlePushNotificationRequest will send a gorush notification and send a response back to the user +func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, + request protobuf.PushNotificationRequest) error { + s.config.Logger.Debug("handling pn request") + response := s.buildPushNotificationRequestResponseAndSendNotification(&request) + if response == nil { + return nil + } + encodedMessage, err := proto.Marshal(response) + if err != nil { + return err + } + + rawMessage := &common.RawMessage{ + Payload: encodedMessage, + MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, + // We skip encryption here as the message has been sent from an ephemeral key + SkipEncryption: true, + } + + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + return err +} + +// buildGrantSignatureMaterial builds a grant for a specific server. +// We use 3 components: +// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid +// 2) The server public key +// 3) The access token +// By verifying this signature, a client can trust the server was instructed to store this access token. +func (s *Server) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte { + var signatureMaterial []byte + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...) + signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...) + signatureMaterial = append(signatureMaterial, []byte(accessToken)...) + a := crypto.Keccak256(signatureMaterial) + return a +} + +func (s *Server) verifyGrantSignature(clientPublicKey *ecdsa.PublicKey, accessToken string, grant []byte) error { + signatureMaterial := s.buildGrantSignatureMaterial(clientPublicKey, &s.config.Identity.PublicKey, accessToken) + recoveredPublicKey, err := crypto.SigToPub(signatureMaterial, grant) + if err != nil { + return err + } + + if !common.IsPubKeyEqual(recoveredPublicKey, clientPublicKey) { + return errors.New("pubkey mismatch") + } + return nil + +} + +func (s *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) { + return ecies.ImportECDSA(s.config.Identity).GenerateShared( ecies.ImportECDSAPublic(publicKey), encryptedPayloadKeyLength, encryptedPayloadKeyLength, ) } -func (p *Server) validateUUID(u string) error { +func (s *Server) validateUUID(u string) error { if len(u) == 0 { return errors.New("empty uuid") } @@ -59,8 +194,8 @@ func (p *Server) validateUUID(u string) error { return err } -func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { - sharedKey, err := p.generateSharedKey(publicKey) +func (s *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) { + sharedKey, err := s.generateSharedKey(publicKey) if err != nil { return nil, err } @@ -68,9 +203,9 @@ func (p *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) return common.Decrypt(payload, sharedKey) } -// ValidateRegistration validates a new message against the last one received for a given installationID and and public key +// validateRegistration validates a new message against the last one received for a given installationID and and public key // and return the decrypted message -func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) { +func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) { if payload == nil { return nil, ErrEmptyPushNotificationRegistrationPayload } @@ -107,7 +242,7 @@ func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrInvalidPushNotificationRegistrationVersion } - // Unregistering message + // unregistering message if registration.Unregister { return registration, nil } @@ -126,7 +261,7 @@ func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return nil, ErrMalformedPushNotificationRegistrationGrant } - if len(registration.Token) == 0 { + if len(registration.DeviceToken) == 0 { return nil, ErrMalformedPushNotificationRegistrationDeviceToken } @@ -137,9 +272,10 @@ func (s *Server) ValidateRegistration(publicKey *ecdsa.PublicKey, payload []byte return registration, nil } -func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { +// buildPushNotificationQueryResponse check if we have the client information and send them back +func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse { - s.config.Logger.Info("handling push notification query") + s.config.Logger.Debug("handling push notification query") response := &protobuf.PushNotificationQueryResponse{} if query == nil || len(query.PublicKeys) == 0 { return response @@ -161,8 +297,9 @@ func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue InstallationId: registration.InstallationId, } + // if instructed to only allow from contacts, send back a list if registration.AllowFromContactsOnly { - info.AllowedUserList = registration.AllowedUserList + info.AllowedKeyList = registration.AllowedKeyList } else { info.AccessToken = registration.AccessToken } @@ -173,8 +310,9 @@ func (s *Server) HandlePushNotificationQuery(query *protobuf.PushNotificationQue return response } -func (s *Server) HandlePushNotificationRequest(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { - s.config.Logger.Info("handling pn request") +// buildPushNotificationRequestResponseAndSendNotification will build a response +// and fire-and-forget send a query to the gorush instance +func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { response := &protobuf.PushNotificationResponse{} // We don't even send a response in this case if request == nil || len(request.MessageId) == 0 { @@ -185,7 +323,7 @@ func (s *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio response.MessageId = request.MessageId // TODO: filter by chat id - // Collect successful requests & registrations + // collect successful requests & registrations var requestAndRegistrations []*RequestAndRegistration for _, pn := range request.Requests { @@ -216,7 +354,7 @@ func (s *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio response.Reports = append(response.Reports, report) } - s.config.Logger.Info("built pn request") + s.config.Logger.Debug("built pn request") if len(requestAndRegistrations) == 0 { s.config.Logger.Warn("no request and registration") return response @@ -224,25 +362,35 @@ func (s *Server) HandlePushNotificationRequest(request *protobuf.PushNotificatio // This can be done asynchronously goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) - //TODO: REMOVE ME - s.config.Logger.Info("REQUEST", zap.Any("REQUEST", goRushRequest)) err := sendGoRushNotification(goRushRequest, s.config.GorushURL) if err != nil { s.config.Logger.Error("failed to send go rush notification", zap.Error(err)) // TODO: handle this error? + // GoRush will not let us know that the sending of the push notification has failed, + // so this likely mean that the actual HTTP request has failed, or there was some unexpected error } return response } -func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { +// listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key +func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { + if s.messageProcessor == nil { + return nil + } + encodedPublicKey := hex.EncodeToString(hashedPublicKey) + return s.messageProcessor.JoinPublic(encodedPublicKey) +} + +// buildPushNotificationRegistrationResponse will check the registration is valid, save it, and listen to the topic for the queries +func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse { s.config.Logger.Info("handling push notification registration") response := &protobuf.PushNotificationRegistrationResponse{ RequestId: common.Shake256(payload), } - registration, err := s.ValidateRegistration(publicKey, payload) + registration, err := s.validateRegistration(publicKey, payload) if err != nil { if err == ErrInvalidPushNotificationRegistrationVersion { @@ -284,140 +432,3 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, return response } - -func (s *Server) Start() error { - s.config.Logger.Info("starting push notification server") - if s.config.Identity == nil { - s.config.Logger.Info("Identity nil") - // Pull identity from database - identity, err := s.persistence.GetIdentity() - if err != nil { - return err - } - if identity == nil { - identity, err = crypto.GenerateKey() - if err != nil { - return err - } - if err := s.persistence.SaveIdentity(identity); err != nil { - return err - } - } - s.config.Identity = identity - } - - pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys() - if err != nil { - return err - } - for _, pk := range pks { - if err := s.listenToPublicKeyQueryTopic(pk); err != nil { - return err - } - } - - s.config.Logger.Info("started push notification server", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&s.config.Identity.PublicKey)))) - - return nil -} - -func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error { - if s.messageProcessor == nil { - return nil - } - encodedPublicKey := hex.EncodeToString(hashedPublicKey) - return s.messageProcessor.JoinPublic(encodedPublicKey) -} - -func (p *Server) HandlePushNotificationRegistration2(publicKey *ecdsa.PublicKey, payload []byte) error { - response := p.HandlePushNotificationRegistration(publicKey, payload) - if response == nil { - return nil - } - encodedMessage, err := proto.Marshal(response) - if err != nil { - return err - } - - rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE, - } - - _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) - return err -} - -func (p *Server) HandlePushNotificationQuery2(publicKey *ecdsa.PublicKey, messageID []byte, query protobuf.PushNotificationQuery) error { - response := p.HandlePushNotificationQuery(&query) - if response == nil { - return nil - } - response.MessageId = messageID - encodedMessage, err := proto.Marshal(response) - if err != nil { - return err - } - - rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE, - SkipNegotiation: true, - } - - _, err = p.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) - return err - -} - -func (s *Server) HandlePushNotificationRequest2(publicKey *ecdsa.PublicKey, - request protobuf.PushNotificationRequest) error { - s.config.Logger.Info("handling pn request") - response := s.HandlePushNotificationRequest(&request) - if response == nil { - return nil - } - encodedMessage, err := proto.Marshal(response) - if err != nil { - return err - } - - rawMessage := &common.RawMessage{ - Payload: encodedMessage, - MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE, - SkipNegotiation: true, - } - - _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) - return err -} - -// buildGrantSignatureMaterial builds a grant for a specific server. -// We use 3 components: -// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid -// 2) The server public key -// 3) The access token -// By verifying this signature, a client can trust the server was instructed to store this access token. - -func (s *Server) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte { - var signatureMaterial []byte - signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...) - signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...) - signatureMaterial = append(signatureMaterial, []byte(accessToken)...) - a := crypto.Keccak256(signatureMaterial) - return a -} - -func (s *Server) verifyGrantSignature(clientPublicKey *ecdsa.PublicKey, accessToken string, grant []byte) error { - signatureMaterial := s.buildGrantSignatureMaterial(clientPublicKey, &s.config.Identity.PublicKey, accessToken) - recoveredPublicKey, err := crypto.SigToPub(signatureMaterial, grant) - if err != nil { - return err - } - - if !common.IsPubKeyEqual(recoveredPublicKey, clientPublicKey) { - return errors.New("pubkey mismatch") - } - return nil - -} diff --git a/protocol/push_notification_server/server_test.go b/protocol/pushnotificationserver/server_test.go similarity index 82% rename from protocol/push_notification_server/server_test.go rename to protocol/pushnotificationserver/server_test.go index f0e3fb1d5..77613ce50 100644 --- a/protocol/push_notification_server/server_test.go +++ b/protocol/pushnotificationserver/server_test.go @@ -1,4 +1,4 @@ -package push_notification_server +package pushnotificationserver import ( "crypto/ecdsa" @@ -7,10 +7,10 @@ import ( "os" "testing" - "github.com/ethereum/go-ethereum/crypto" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/suite" + "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" "github.com/status-im/status-go/protocol/sqlite" @@ -76,35 +76,35 @@ func (s *ServerSuite) SetupTest() { func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { // Empty payload - _, err := s.server.ValidateRegistration(&s.key.PublicKey, nil) + _, err := s.server.validateRegistration(&s.key.PublicKey, nil) s.Require().Equal(ErrEmptyPushNotificationRegistrationPayload, err) // Empty key - _, err = s.server.ValidateRegistration(nil, []byte("payload")) + _, err = s.server.validateRegistration(nil, []byte("payload")) s.Require().Equal(ErrEmptyPushNotificationRegistrationPublicKey, err) // Invalid cyphertext length - _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) + _, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short")) s.Require().Equal(common.ErrInvalidCiphertextLength, err) // Invalid cyphertext length - _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("too short")) + _, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short")) s.Require().Equal(common.ErrInvalidCiphertextLength, err) // Invalid ciphertext - _, err = s.server.ValidateRegistration(&s.key.PublicKey, []byte("not too short but invalid")) + _, err = s.server.validateRegistration(&s.key.PublicKey, []byte("not too short but invalid")) s.Require().Error(common.ErrInvalidCiphertextLength, err) // Different key ciphertext cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Error(err) // Right cyphertext but non unmarshable payload cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err) // Missing installationID @@ -118,7 +118,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Malformed installationID @@ -129,9 +129,11 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { InstallationId: "abc", Version: 1, }) + s.Require().NoError(err) + cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err) // Version set to 0 @@ -145,7 +147,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Version lower than previous one @@ -169,7 +171,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { InstallationId: s.installationID, Version: 2})) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err) // Cleanup persistence @@ -185,7 +187,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Nil(err) // Missing access token @@ -199,7 +201,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Invalid access token @@ -214,7 +216,7 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err) // Missing device token @@ -229,13 +231,13 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err) // Missing grant payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, - Token: "device-token", + DeviceToken: "device-token", InstallationId: s.installationID, Version: 1, }) @@ -243,14 +245,14 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err) // Invalid grant payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, - Token: "device-token", + DeviceToken: "device-token", Grant: crypto.Keccak256([]byte("invalid")), InstallationId: s.installationID, Version: 1, @@ -259,13 +261,13 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err) // Missing token type payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ AccessToken: s.accessToken, - Token: "device-token", + DeviceToken: "device-token", Grant: s.grant, InstallationId: s.installationID, Version: 1, @@ -274,12 +276,12 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().Equal(ErrUnknownPushNotificationRegistrationTokenType, err) // Successful payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - Token: "abc", + DeviceToken: "abc", AccessToken: s.accessToken, Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, @@ -290,37 +292,37 @@ func (s *ServerSuite) TestPushNotificationServerValidateRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - _, err = s.server.ValidateRegistration(&s.key.PublicKey, cyphertext) + _, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext) s.Require().NoError(err) } func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Empty payload - response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, nil) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, nil) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Empty key - response = s.server.HandlePushNotificationRegistration(nil, []byte("payload")) + response = s.server.buildPushNotificationRegistrationResponse(nil, []byte("payload")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid cyphertext length - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("too short")) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid cyphertext length - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("too short")) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Invalid ciphertext - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, []byte("not too short but invalid")) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("not too short but invalid")) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -328,7 +330,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Different key ciphertext cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -336,7 +338,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Right cyphertext but non unmarshable payload cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -351,7 +353,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -363,9 +365,10 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { Grant: s.grant, Version: 1, }) + s.Require().NoError(err) cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -380,7 +383,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) @@ -404,7 +407,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { InstallationId: s.installationID, Version: 2})) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH) @@ -422,7 +425,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -438,7 +441,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) @@ -454,14 +457,14 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().False(response.Success) s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE) // Successful registration := &protobuf.PushNotificationRegistration{ - Token: "abc", + DeviceToken: "abc", AccessToken: s.accessToken, Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, @@ -473,7 +476,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) @@ -485,7 +488,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { // Unregistering message payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{ - Token: "token", + DeviceToken: "token", InstallationId: s.installationID, Unregister: true, Version: 2, @@ -494,7 +497,7 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response = s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) @@ -503,17 +506,17 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() { s.Require().NoError(err) s.Require().NotNil(retrievedRegistration) s.Require().Empty(retrievedRegistration.AccessToken) - s.Require().Empty(retrievedRegistration.Token) + s.Require().Empty(retrievedRegistration.DeviceToken) s.Require().Equal(uint64(2), retrievedRegistration.Version) s.Require().Equal(s.installationID, retrievedRegistration.InstallationId) s.Require().Equal(common.Shake256(cyphertext), response.RequestId) } -func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { +func (s *ServerSuite) TestbuildPushNotificationQueryResponseNoFiltering() { hashedPublicKey := common.HashPublicKey(&s.key.PublicKey) // Successful registration := &protobuf.PushNotificationRegistration{ - Token: "abc", + DeviceToken: "abc", AccessToken: s.accessToken, Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, @@ -525,7 +528,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) @@ -533,29 +536,29 @@ func (s *ServerSuite) TestHandlePushNotificationQueryNoFiltering() { PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey}, } - queryResponse := s.server.HandlePushNotificationQuery(query) + queryResponse := s.server.buildPushNotificationQueryResponse(query) s.Require().NotNil(queryResponse) s.Require().True(queryResponse.Success) s.Require().Len(queryResponse.Info, 1) s.Require().Equal(s.accessToken, queryResponse.Info[0].AccessToken) s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey) s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) - s.Require().Nil(queryResponse.Info[0].AllowedUserList) + s.Require().Nil(queryResponse.Info[0].AllowedKeyList) } -func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { +func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() { hashedPublicKey := common.HashPublicKey(&s.key.PublicKey) - allowedUserList := [][]byte{[]byte("a")} + allowedKeyList := [][]byte{[]byte("a")} // Successful registration := &protobuf.PushNotificationRegistration{ - Token: "abc", + DeviceToken: "abc", AccessToken: s.accessToken, Grant: s.grant, TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, InstallationId: s.installationID, AllowFromContactsOnly: true, - AllowedUserList: allowedUserList, + AllowedKeyList: allowedKeyList, Version: 1, } payload, err := proto.Marshal(registration) @@ -563,7 +566,7 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) s.Require().NoError(err) - response := s.server.HandlePushNotificationRegistration(&s.key.PublicKey, cyphertext) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) s.Require().NotNil(response) s.Require().True(response.Success) @@ -571,11 +574,11 @@ func (s *ServerSuite) TestHandlePushNotificationQueryWithFiltering() { PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey}, } - queryResponse := s.server.HandlePushNotificationQuery(query) + queryResponse := s.server.buildPushNotificationQueryResponse(query) s.Require().NotNil(queryResponse) s.Require().True(queryResponse.Success) s.Require().Len(queryResponse.Info, 1) s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey) s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) - s.Require().Equal(allowedUserList, queryResponse.Info[0].AllowedUserList) + s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList) } diff --git a/protocol/sqlite/migrations.go b/protocol/sqlite/migrations.go index 50b381a6d..5c9eb035d 100644 --- a/protocol/sqlite/migrations.go +++ b/protocol/sqlite/migrations.go @@ -7,8 +7,8 @@ import ( encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations" appmigrations "github.com/status-im/status-go/protocol/migrations" - push_notification_client_migrations "github.com/status-im/status-go/protocol/push_notification_client/migrations" - push_notification_server_migrations "github.com/status-im/status-go/protocol/push_notification_server/migrations" + push_notification_client_migrations "github.com/status-im/status-go/protocol/pushnotificationclient/migrations" + push_notification_server_migrations "github.com/status-im/status-go/protocol/pushnotificationserver/migrations" wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations" whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations" ) diff --git a/services/ext/api.go b/services/ext/api.go index 366f91b96..ffb160c61 100644 --- a/services/ext/api.go +++ b/services/ext/api.go @@ -17,7 +17,7 @@ import ( "github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/protocol" "github.com/status-im/status-go/protocol/encryption/multidevice" - "github.com/status-im/status-go/protocol/push_notification_client" + "github.com/status-im/status-go/protocol/pushnotificationclient" "github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/services/ext/mailservers" ) @@ -403,30 +403,31 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error { return api.service.UpdateMailservers(nodes) } -// PushNotifications server +// PushNotifications server endpoints -func (api *PublicAPI) StartPushNotificationServer() error { - err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled", true) +func (api *PublicAPI) StartPushNotificationsServer() error { + err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", true) if err != nil { return err } - return api.service.messenger.StartPushNotificationServer() + return api.service.messenger.StartPushNotificationsServer() } -func (api *PublicAPI) StopPushNotificationServer() error { - err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled", false) +func (api *PublicAPI) StopPushNotificationsServer() error { + err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", false) if err != nil { return err } - return api.service.messenger.StopPushNotificationServer() + return api.service.messenger.StopPushNotificationsServer() } -// PushNotification client +// PushNotification client endpoints func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error { - err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled", true) + // We set both for now as they are equivalent + err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", true) if err != nil { return err } @@ -439,7 +440,7 @@ func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceTo } func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error { - err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled", false) + err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", false) if err != nil { return err } @@ -452,7 +453,7 @@ func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error } func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error { - err := api.service.accountsDB.SaveSetting("send-push-notifications", false) + err := api.service.accountsDB.SaveSetting("send-push-notifications?", false) if err != nil { return err } @@ -461,7 +462,7 @@ func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error { } func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error { - err := api.service.accountsDB.SaveSetting("send-push-notifications", true) + err := api.service.accountsDB.SaveSetting("send-push-notifications?", true) if err != nil { return err } @@ -469,7 +470,7 @@ func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error { } func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Context) error { - err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only", true) + err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", true) if err != nil { return err } @@ -477,20 +478,20 @@ func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Contex } func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Context) error { - err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only", false) + err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", false) if err != nil { return err } return api.service.messenger.DisablePushNotificationsFromContactsOnly() } -func (api *PublicAPI) AddPushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { +func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error { publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes) if err != nil { return err } - return api.service.messenger.AddPushNotificationServer(ctx, publicKey) + return api.service.messenger.AddPushNotificationsServer(ctx, publicKey) } func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error { @@ -502,7 +503,7 @@ func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKe return api.service.messenger.RemovePushNotificationServer(ctx, publicKey) } -func (api *PublicAPI) GetPushNotificationServers() ([]*push_notification_client.PushNotificationServer, error) { +func (api *PublicAPI) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) { return api.service.messenger.GetPushNotificationServers() } diff --git a/services/ext/service.go b/services/ext/service.go index 86236adcb..c39741c0c 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -33,8 +33,8 @@ import ( coretypes "github.com/status-im/status-go/eth-node/core/types" "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol" - "github.com/status-im/status-go/protocol/push_notification_client" - "github.com/status-im/status-go/protocol/push_notification_server" + "github.com/status-im/status-go/protocol/pushnotificationclient" + "github.com/status-im/status-go/protocol/pushnotificationserver" "github.com/status-im/status-go/protocol/transport" ) @@ -465,19 +465,20 @@ func buildMessengerOptions( options = append(options, protocol.WithDatasync()) } settings, err := accountsDB.GetSettings() - if err != nil { + if err != sql.ErrNoRows && err != nil { return nil, err } if settings.PushNotificationsServerEnabled { - config := &push_notification_server.Config{ + config := &pushnotificationserver.Config{ Logger: logger, } options = append(options, protocol.WithPushNotificationServerConfig(config)) } - options = append(options, protocol.WithPushNotificationClientConfig(&push_notification_client.Config{ + options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{ SendEnabled: settings.SendPushNotifications, + AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly, RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled, })) diff --git a/services/shhext/api_geth_test.go b/services/shhext/api_geth_test.go index 30d5ed990..b33b454b3 100644 --- a/services/shhext/api_geth_test.go +++ b/services/shhext/api_geth_test.go @@ -24,12 +24,12 @@ import ( "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/storage" + "github.com/status-im/status-go/appdatabase" 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/params" "github.com/status-im/status-go/services/ext" - "github.com/status-im/status-go/sqlite" "github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/whisper/v6" ) @@ -209,7 +209,7 @@ func TestInitProtocol(t *testing.T) { tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol") require.NoError(t, err) - sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password") + sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password") require.NoError(t, err) err = service.InitProtocol(privateKey, sqlDB, zap.NewNop()) @@ -262,7 +262,7 @@ func (s *ShhExtSuite) createAndAddNode() { s.Require().NoError(err) nodeWrapper := ext.NewTestNodeWrapper(gethbridge.NewGethWhisperWrapper(whisper), nil) service := New(config, nodeWrapper, nil, nil, db) - sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password") + sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password") s.Require().NoError(err) privateKey, err := crypto.GenerateKey() s.NoError(err) diff --git a/services/wakuext/api_test.go b/services/wakuext/api_test.go index a8fb1a894..71231602d 100644 --- a/services/wakuext/api_test.go +++ b/services/wakuext/api_test.go @@ -23,12 +23,12 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/status-im/status-go/appdatabase" 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/params" "github.com/status-im/status-go/services/ext" - "github.com/status-im/status-go/sqlite" "github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/waku" wakucommon "github.com/status-im/status-go/waku/common" @@ -126,7 +126,7 @@ func TestInitProtocol(t *testing.T) { tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol") require.NoError(t, err) - sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password") + sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password") require.NoError(t, err) err = service.InitProtocol(privateKey, sqlDB, zap.NewNop()) @@ -179,7 +179,7 @@ func (s *ShhExtSuite) createAndAddNode() { s.Require().NoError(err) nodeWrapper := ext.NewTestNodeWrapper(nil, gethbridge.NewGethWakuWrapper(w)) service := New(config, nodeWrapper, nil, nil, db) - sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password") + sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password") s.Require().NoError(err) privateKey, err := crypto.GenerateKey() s.NoError(err) diff --git a/static/bindata.go b/static/bindata.go index c11101613..86f0a3b07 100644 --- a/static/bindata.go +++ b/static/bindata.go @@ -97,7 +97,7 @@ func ConfigReadmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)} + info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd0, 0x71, 0x14, 0x3e, 0x9d, 0x9c, 0x11, 0x40, 0xe1, 0xe9, 0x8b, 0xcc, 0x38, 0x17, 0x69, 0xb, 0xa7, 0xf2, 0x91, 0xa6, 0x4a, 0x9f, 0xd2, 0xc6, 0xf4, 0xff, 0x37, 0x5d, 0xd3, 0xed, 0x7c, 0xbd}} return a, nil } @@ -117,7 +117,7 @@ func ConfigCliFleetEthProdJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)} + info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x86, 0x93, 0x3b, 0x8c, 0xb4, 0x1b, 0x8b, 0x70, 0xe6, 0x90, 0x3, 0xc9, 0x93, 0x99, 0x97, 0x18, 0xb7, 0x49, 0xbf, 0x2d, 0x35, 0x98, 0xb8, 0x49, 0xa8, 0x73, 0x30, 0xf5, 0x2d, 0x7f, 0x5c}} return a, nil } @@ -137,7 +137,7 @@ func ConfigCliFleetEthStagingJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)} + info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xef, 0x3d, 0x5c, 0x41, 0xb1, 0xc8, 0x54, 0xd3, 0x82, 0x60, 0xcf, 0x5f, 0x8b, 0x92, 0xd0, 0x41, 0xa7, 0xff, 0xfd, 0x2c, 0x95, 0x44, 0x68, 0xd3, 0xad, 0x16, 0x8c, 0x49, 0x10, 0x4e, 0xb7}} return a, nil } @@ -157,7 +157,7 @@ func ConfigCliFleetEthTestJson() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)} + info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe4, 0x90, 0x4, 0x6, 0x2b, 0xe4, 0x67, 0x7a, 0xfc, 0x70, 0xa9, 0xb, 0x85, 0x8c, 0xb3, 0x8, 0x8e, 0xee, 0xec, 0x64, 0xb7, 0xac, 0x4c, 0x6, 0x4d, 0xcc, 0x4f, 0xda, 0xbd, 0xed, 0x17, 0x53}} return a, nil } From c61bf0cd9c42f327f0f9e26eb7e563d2c108af94 Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Mon, 27 Jul 2020 08:55:20 +0200 Subject: [PATCH 46/46] bump version --- VERSION | 2 +- multiaccounts/accounts/database_test.go | 27 ++++++++++++----------- peers/peerpool_test.go | 29 +++++++++++++++++++++---- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/VERSION b/VERSION index 9dd7871a6..2f4c74eb2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.55.3 +0.56.0 diff --git a/multiaccounts/accounts/database_test.go b/multiaccounts/accounts/database_test.go index 6d26761ba..2725d821e 100644 --- a/multiaccounts/accounts/database_test.go +++ b/multiaccounts/accounts/database_test.go @@ -22,19 +22,20 @@ var ( networks = json.RawMessage("{}") settings = Settings{ - Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"), - CurrentNetwork: "mainnet_rpc", - DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"), - InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", - KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab", - LatestDerivedPath: 0, - Name: "Jittery Cornflowerblue Kingbird", - Networks: &networks, - PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", - PreviewPrivacy: false, - PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0", - SigningPhrase: "yurt joey vibe", - WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")} + Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"), + CurrentNetwork: "mainnet_rpc", + DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"), + InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", + KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab", + LatestDerivedPath: 0, + Name: "Jittery Cornflowerblue Kingbird", + Networks: &networks, + PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==", + PreviewPrivacy: false, + PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0", + SigningPhrase: "yurt joey vibe", + SendPushNotifications: true, + WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")} ) func setupTestDB(t *testing.T) (*Database, func()) { diff --git a/peers/peerpool_test.go b/peers/peerpool_test.go index d8b8b8b26..45e7018e8 100644 --- a/peers/peerpool_test.go +++ b/peers/peerpool_test.go @@ -30,6 +30,21 @@ import ( "github.com/status-im/status-go/whisper/v6" ) +// GetFreePort asks the kernel for a free open port that is ready to use. +func getFreePort() (int, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil +} + type PeerPoolSimulationSuite struct { suite.Suite @@ -41,14 +56,20 @@ type PeerPoolSimulationSuite struct { } func TestPeerPoolSimulationSuite(t *testing.T) { - s := new(PeerPoolSimulationSuite) - s.port = 33731 + s := &PeerPoolSimulationSuite{} + port, err := getFreePort() + if err != nil { + panic(err) + } + s.port = uint16(port) + suite.Run(t, s) } func (s *PeerPoolSimulationSuite) nextPort() uint16 { - s.port++ - return s.port + port, err := getFreePort() + s.Require().NoError(err) + return uint16(port) } func (s *PeerPoolSimulationSuite) SetupTest() {