diff --git a/protocol/messenger.go b/protocol/messenger.go index 4512706ce..23fb3a49a 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -2125,7 +2125,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte continue } logger.Debug("Handling PushNotificationRequest") - if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ParsedMessage.Interface().(protobuf.PushNotificationRequest)); err != nil { + if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ID, msg.ParsedMessage.Interface().(protobuf.PushNotificationRequest)); err != nil { logger.Warn("failed to handle PushNotificationRequest", zap.Error(err)) } // We continue in any case, no changes to messenger diff --git a/protocol/pushnotificationserver/gorush.go b/protocol/pushnotificationserver/gorush.go index 2598a6159..18b28ce71 100644 --- a/protocol/pushnotificationserver/gorush.go +++ b/protocol/pushnotificationserver/gorush.go @@ -2,7 +2,6 @@ package pushnotificationserver import ( "bytes" - "encoding/hex" "encoding/json" "io/ioutil" "net/http" @@ -60,9 +59,9 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ Message: defaultNotificationMessage, Topic: registration.ApnTopic, Data: &GoRushRequestData{ - EncryptedMessage: hex.EncodeToString(request.Message), + EncryptedMessage: types.EncodeHex(request.Message), ChatID: types.EncodeHex(request.ChatId), - PublicKey: hex.EncodeToString(request.PublicKey), + PublicKey: types.EncodeHex(request.PublicKey), }, }) } diff --git a/protocol/pushnotificationserver/gorush_test.go b/protocol/pushnotificationserver/gorush_test.go index f60b21a01..e6e99128e 100644 --- a/protocol/pushnotificationserver/gorush_test.go +++ b/protocol/pushnotificationserver/gorush_test.go @@ -1,11 +1,11 @@ package pushnotificationserver import ( - "encoding/hex" "testing" "github.com/stretchr/testify/require" + "github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/protocol/protobuf" ) @@ -13,10 +13,10 @@ 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" + hexMessage1 := types.EncodeHex(message1) + hexMessage2 := types.EncodeHex(message2) + hexMessage3 := types.EncodeHex(message3) + chatID := []byte("chat-id") publicKey1 := []byte("public-key-1") publicKey2 := []byte("public-key-2") installationID1 := "installation-id-1" @@ -76,8 +76,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: defaultNotificationMessage, Data: &GoRushRequestData{ EncryptedMessage: hexMessage1, - ChatID: chatID, - PublicKey: hex.EncodeToString(publicKey1), + ChatID: types.EncodeHex(chatID), + PublicKey: types.EncodeHex(publicKey1), }, }, { @@ -86,8 +86,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: defaultNotificationMessage, Data: &GoRushRequestData{ EncryptedMessage: hexMessage2, - ChatID: chatID, - PublicKey: hex.EncodeToString(publicKey1), + ChatID: types.EncodeHex(chatID), + PublicKey: types.EncodeHex(publicKey1), }, }, { @@ -96,8 +96,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) { Message: defaultNotificationMessage, Data: &GoRushRequestData{ EncryptedMessage: hexMessage3, - ChatID: chatID, - PublicKey: hex.EncodeToString(publicKey2), + ChatID: types.EncodeHex(chatID), + PublicKey: types.EncodeHex(publicKey2), }, }, }, diff --git a/protocol/pushnotificationserver/migrations/migrations.go b/protocol/pushnotificationserver/migrations/migrations.go index 1fcba9bb4..0340c83fa 100644 --- a/protocol/pushnotificationserver/migrations/migrations.go +++ b/protocol/pushnotificationserver/migrations/migrations.go @@ -2,6 +2,8 @@ // sources: // 1593601728_initial_schema.down.sql (200B) // 1593601728_initial_schema.up.sql (675B) +// 1598419937_add_push_notifications_table.down.sql (51B) +// 1598419937_add_push_notifications_table.up.sql (104B) // doc.go (382B) package migrations @@ -86,7 +88,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(1595840384, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 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,11 +108,51 @@ 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(1595840384, 0)} + info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 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 } +var __1598419937_add_push_notifications_tableDownSql = []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\x42\x11\x2b\xb6\xe6\x02\x04\x00\x00\xff\xff\xb7\xdc\x38\x53\x33\x00\x00\x00") + +func _1598419937_add_push_notifications_tableDownSqlBytes() ([]byte, error) { + return bindataRead( + __1598419937_add_push_notifications_tableDownSql, + "1598419937_add_push_notifications_table.down.sql", + ) +} + +func _1598419937_add_push_notifications_tableDownSql() (*asset, error) { + bytes, err := _1598419937_add_push_notifications_tableDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1598419937_add_push_notifications_table.down.sql", size: 51, mode: os.FileMode(0644), modTime: time.Unix(1598420015, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x98, 0xc8, 0x30, 0x45, 0x5b, 0xc5, 0x7d, 0x13, 0x5d, 0xe7, 0xc8, 0x23, 0x43, 0xf7, 0xdc, 0x9c, 0xe2, 0xdd, 0x63, 0xf0, 0xb7, 0x16, 0x40, 0xc, 0xda, 0xb9, 0x16, 0x70, 0x2b, 0x5a, 0x7e}} + return a, nil +} + +var __1598419937_add_push_notifications_tableUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x0e\x72\x75\x0c\x71\x55\x08\x71\x74\xf2\x71\x55\xf0\x74\x53\xf0\xf3\x0f\x51\x70\x8d\xf0\x0c\x0e\x09\x56\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x42\x11\x2b\x56\xd0\xe0\x52\x50\xc8\x4c\x51\x70\xf2\xf1\x77\x02\xeb\xf6\x0b\xf5\xf1\xd1\xe1\x52\x50\x08\xf5\xf3\x0c\x0c\x75\xd5\xc8\x4c\xd1\xe4\xd2\xb4\xe6\x02\x04\x00\x00\xff\xff\xf3\xc8\x52\x6b\x68\x00\x00\x00") + +func _1598419937_add_push_notifications_tableUpSqlBytes() ([]byte, error) { + return bindataRead( + __1598419937_add_push_notifications_tableUpSql, + "1598419937_add_push_notifications_table.up.sql", + ) +} + +func _1598419937_add_push_notifications_tableUpSql() (*asset, error) { + bytes, err := _1598419937_add_push_notifications_tableUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1598419937_add_push_notifications_table.up.sql", size: 104, mode: os.FileMode(0644), modTime: time.Unix(1598419975, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2, 0x3e, 0xef, 0xf, 0xc2, 0xdf, 0xbc, 0x99, 0x7a, 0xc2, 0xd3, 0x64, 0x4f, 0x4c, 0x7e, 0xfc, 0x2e, 0x8c, 0xa7, 0x54, 0xd3, 0x4d, 0x25, 0x98, 0x41, 0xbc, 0xea, 0xd7, 0x2, 0xc1, 0xd0, 0x52}} + 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) { @@ -126,7 +168,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595840384, 0)} + info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 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 } @@ -226,6 +268,10 @@ var _bindata = map[string]func() (*asset, error){ "1593601728_initial_schema.up.sql": _1593601728_initial_schemaUpSql, + "1598419937_add_push_notifications_table.down.sql": _1598419937_add_push_notifications_tableDownSql, + + "1598419937_add_push_notifications_table.up.sql": _1598419937_add_push_notifications_tableUpSql, + "doc.go": docGo, } @@ -270,9 +316,11 @@ type bintree struct { } 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{}}, + "1593601728_initial_schema.down.sql": &bintree{_1593601728_initial_schemaDownSql, map[string]*bintree{}}, + "1593601728_initial_schema.up.sql": &bintree{_1593601728_initial_schemaUpSql, map[string]*bintree{}}, + "1598419937_add_push_notifications_table.down.sql": &bintree{_1598419937_add_push_notifications_tableDownSql, map[string]*bintree{}}, + "1598419937_add_push_notifications_table.up.sql": &bintree{_1598419937_add_push_notifications_tableUpSql, map[string]*bintree{}}, + "doc.go": &bintree{docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.down.sql b/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.down.sql new file mode 100644 index 000000000..1ccda221a --- /dev/null +++ b/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.down.sql @@ -0,0 +1 @@ +DROP TABLE push_notification_server_notifications; diff --git a/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.up.sql b/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.up.sql new file mode 100644 index 000000000..a2f74c1c0 --- /dev/null +++ b/protocol/pushnotificationserver/migrations/sql/1598419937_add_push_notifications_table.up.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS push_notification_server_notifications ( + id BLOB NOT NULL, + UNIQUE(id) +); diff --git a/protocol/pushnotificationserver/persistence.go b/protocol/pushnotificationserver/persistence.go index 7ba19d99b..95b4a4d71 100644 --- a/protocol/pushnotificationserver/persistence.go +++ b/protocol/pushnotificationserver/persistence.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/golang/protobuf/proto" + sqlite3 "github.com/mutecomm/go-sqlcipher" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/protocol/protobuf" @@ -32,6 +33,8 @@ type Persistence interface { GetIdentity() (*ecdsa.PrivateKey, error) // SaveIdentity saves the server identity key SaveIdentity(*ecdsa.PrivateKey) error + // PushNotificationExists checks whether a push notification exists and inserts it otherwise + PushNotificationExists([]byte) (bool, error) } type SQLitePersistence struct { @@ -178,3 +181,14 @@ func (p *SQLitePersistence) GetIdentity() (*ecdsa.PrivateKey, error) { } return pk, nil } + +func (p *SQLitePersistence) PushNotificationExists(messageID []byte) (bool, error) { + _, err := p.db.Exec(`INSERT INTO push_notification_server_notifications VALUES (?)`, messageID) + if err != nil && err.(sqlite3.Error).ExtendedCode == sqlite3.ErrConstraintUnique { + return true, nil + } else if err != nil { + return false, err + } + + return false, nil +} diff --git a/protocol/pushnotificationserver/persistence_test.go b/protocol/pushnotificationserver/persistence_test.go index 31029f99d..4a39140cb 100644 --- a/protocol/pushnotificationserver/persistence_test.go +++ b/protocol/pushnotificationserver/persistence_test.go @@ -81,3 +81,22 @@ func (s *SQLitePersistenceSuite) TestSaveDifferentIdenities() { s.Require().NoError(s.persistence.SaveIdentity(key1)) s.Require().Error(s.persistence.SaveIdentity(key2)) } + +func (s *SQLitePersistenceSuite) TestExists() { + messageID1 := []byte("1") + messageID2 := []byte("2") + + result, err := s.persistence.PushNotificationExists(messageID1) + s.Require().NoError(err) + s.Require().False(result) + + result, err = s.persistence.PushNotificationExists(messageID1) + s.Require().NoError(err) + + s.Require() + s.Require().True(result) + + result, err = s.persistence.PushNotificationExists(messageID2) + s.Require().NoError(err) + s.Require().False(result) +} diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index 2e341a6ee..e239278a0 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -137,8 +137,21 @@ func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, message // HandlePushNotificationRequest will send a gorush notification and send a response back to the user func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, + messageID []byte, request protobuf.PushNotificationRequest) error { - s.config.Logger.Info("handling pn request") + s.config.Logger.Info("handling pn request", zap.Binary("message-id", messageID)) + + // This is at-most-once semantic for now + exists, err := s.persistence.PushNotificationExists(messageID) + if err != nil { + return err + } + + if exists { + s.config.Logger.Info("already handled") + return nil + } + response := s.buildPushNotificationRequestResponseAndSendNotification(&request) if response == nil { return nil