mirror of
https://github.com/status-im/status-go.git
synced 2025-02-22 03:38:27 +00:00
Merge branch 'feature/push-notifications-fix' into develop
This commit is contained in:
commit
6fb5cd2154
@ -18,6 +18,7 @@ import (
|
||||
"github.com/status-im/status-go/protocol/datasync"
|
||||
datasyncpeer "github.com/status-im/status-go/protocol/datasync/peer"
|
||||
"github.com/status-im/status-go/protocol/encryption"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
@ -60,6 +61,9 @@ type MessageProcessor struct {
|
||||
scheduledMessagesSubscriptions []chan<- *RawMessage
|
||||
|
||||
featureFlags FeatureFlags
|
||||
|
||||
// handleSharedSecrets is a callback that is called every time a new shared secret is negotiated
|
||||
handleSharedSecrets func([]*sharedsecret.Secret) error
|
||||
}
|
||||
|
||||
func NewMessageProcessor(
|
||||
@ -110,9 +114,14 @@ func (p *MessageProcessor) Stop() {
|
||||
for _, c := range p.sentMessagesSubscriptions {
|
||||
close(c)
|
||||
}
|
||||
p.sentMessagesSubscriptions = nil
|
||||
p.datasync.Stop() // idempotent op
|
||||
}
|
||||
|
||||
func (p *MessageProcessor) SetHandleSharedSecrets(handler func([]*sharedsecret.Secret) error) {
|
||||
p.handleSharedSecrets = handler
|
||||
}
|
||||
|
||||
// SendPrivate takes encoded data, encrypts it and sends through the wire.
|
||||
func (p *MessageProcessor) SendPrivate(
|
||||
ctx context.Context,
|
||||
@ -203,7 +212,7 @@ func (p *MessageProcessor) sendPrivate(
|
||||
} 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)
|
||||
hash, newMessage, err := p.sendPrivateRawMessage(ctx, recipient, wrappedMessage, messageIDs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||
}
|
||||
@ -216,6 +225,16 @@ func (p *MessageProcessor) sendPrivate(
|
||||
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
// The shared secret needs to be handle before we send a message
|
||||
// otherwise the topic might not be set up before we receive a message
|
||||
if p.handleSharedSecrets != nil {
|
||||
err := p.handleSharedSecrets([]*sharedsecret.Secret{messageSpec.SharedSecret})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
messageIDs := [][]byte{messageID}
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
|
||||
if err != nil {
|
||||
@ -305,11 +324,23 @@ func (p *MessageProcessor) SendPublic(
|
||||
return nil, errors.Wrap(err, "failed to wrap message")
|
||||
}
|
||||
|
||||
newMessage := &types.NewMessage{
|
||||
TTL: whisperTTL,
|
||||
Payload: wrappedMessage,
|
||||
PowTarget: calculatePoW(wrappedMessage),
|
||||
PowTime: whisperPoWTime,
|
||||
var newMessage *types.NewMessage
|
||||
if !rawMessage.SkipEncryption {
|
||||
messageSpec, err := p.protocol.BuildPublicMessage(p.identity, wrappedMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap a public message in the encryption layer")
|
||||
}
|
||||
newMessage, err = MessageSpecToWhisper(messageSpec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
newMessage = &types.NewMessage{
|
||||
TTL: whisperTTL,
|
||||
Payload: wrappedMessage,
|
||||
PowTarget: calculatePoW(wrappedMessage),
|
||||
PowTime: whisperPoWTime,
|
||||
}
|
||||
}
|
||||
|
||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||
@ -479,6 +510,16 @@ func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
|
||||
return errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
// The shared secret needs to be handle before we send a message
|
||||
// otherwise the topic might not be set up before we receive a message
|
||||
if p.handleSharedSecrets != nil {
|
||||
err := p.handleSharedSecrets([]*sharedsecret.Secret{messageSpec.SharedSecret})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec, messageIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -489,8 +530,8 @@ 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) {
|
||||
// sendPrivateRawMessage sends a message not wrapped in an encryption layer
|
||||
func (p *MessageProcessor) sendPrivateRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||
newMessage := &types.NewMessage{
|
||||
TTL: whisperTTL,
|
||||
Payload: payload,
|
||||
@ -517,11 +558,11 @@ func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa
|
||||
|
||||
var hash []byte
|
||||
|
||||
switch {
|
||||
case messageSpec.SharedSecret != nil:
|
||||
// process shared secret
|
||||
if messageSpec.AgreedSecret {
|
||||
logger.Debug("sending using shared secret")
|
||||
hash, err = p.transport.SendPrivateWithSharedSecret(ctx, newMessage, publicKey, messageSpec.SharedSecret)
|
||||
default:
|
||||
hash, err = p.transport.SendPrivateWithSharedSecret(ctx, newMessage, publicKey, messageSpec.SharedSecret.Key)
|
||||
} else {
|
||||
logger.Debug("sending partitioned topic")
|
||||
hash, err = p.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey)
|
||||
}
|
||||
|
@ -17,8 +17,6 @@ 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/encryption"
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
transport "github.com/status-im/status-go/protocol/transport/whisper"
|
||||
@ -63,15 +61,9 @@ func (s *MessageProcessorSuite) SetupTest() {
|
||||
database, err := sqlite.Open(filepath.Join(s.tmpDir, "processor-test.sql"), "some-key")
|
||||
s.Require().NoError(err)
|
||||
|
||||
onNewInstallations := func([]*multidevice.Installation) {}
|
||||
onNewSharedSecret := func([]*sharedsecret.Secret) {}
|
||||
onSendContactCode := func(*encryption.ProtocolMessageSpec) {}
|
||||
encryptionProtocol := encryption.New(
|
||||
database,
|
||||
"installation-1",
|
||||
onNewInstallations,
|
||||
onNewSharedSecret,
|
||||
onSendContactCode,
|
||||
s.logger,
|
||||
)
|
||||
|
||||
@ -206,9 +198,6 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
|
||||
senderEncryptionProtocol := encryption.New(
|
||||
senderDatabase,
|
||||
"installation-2",
|
||||
func([]*multidevice.Installation) {},
|
||||
func([]*sharedsecret.Secret) {},
|
||||
func(*encryption.ProtocolMessageSpec) {},
|
||||
s.logger,
|
||||
)
|
||||
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -65,9 +64,6 @@ func setupUser(user string, s *EncryptionServiceMultiDeviceSuite, n int) error {
|
||||
protocol := New(
|
||||
db,
|
||||
installationID,
|
||||
func(s []*multidevice.Installation) {},
|
||||
func(s []*sharedsecret.Secret) {},
|
||||
func(*ProtocolMessageSpec) {},
|
||||
s.logger.With(zap.String("user", user)),
|
||||
)
|
||||
s.services[user].services[i] = protocol
|
||||
|
@ -18,9 +18,6 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
)
|
||||
|
||||
var cleartext = []byte("hello")
|
||||
@ -57,9 +54,6 @@ func (s *EncryptionServiceTestSuite) initDatabases(config encryptorConfig) {
|
||||
db,
|
||||
aliceInstallationID,
|
||||
config,
|
||||
func(s []*multidevice.Installation) {},
|
||||
func(s []*sharedsecret.Secret) {},
|
||||
func(*ProtocolMessageSpec) {},
|
||||
s.logger.With(zap.String("user", "alice")),
|
||||
)
|
||||
|
||||
@ -70,9 +64,6 @@ func (s *EncryptionServiceTestSuite) initDatabases(config encryptorConfig) {
|
||||
db,
|
||||
bobInstallationID,
|
||||
config,
|
||||
func(s []*multidevice.Installation) {},
|
||||
func(s []*sharedsecret.Secret) {},
|
||||
func(*ProtocolMessageSpec) {},
|
||||
s.logger.With(zap.String("user", "bob")),
|
||||
)
|
||||
}
|
||||
@ -130,7 +121,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
|
||||
// On the receiver side, we should be able to decrypt using our private key and the ephemeral just sent
|
||||
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using DH")
|
||||
s.Equal(cleartext, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using DH")
|
||||
|
||||
// The next message will not be re-using the same key
|
||||
response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext)
|
||||
@ -147,7 +138,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
|
||||
|
||||
decryptedPayload2, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response2.Message, defaultMessageID)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload2, "It correctly decrypts the payload using DH")
|
||||
s.Equal(cleartext, decryptedPayload2.DecryptedMessage, "It correctly decrypts the payload using DH")
|
||||
}
|
||||
|
||||
// Alice has Bob's bundle
|
||||
@ -201,7 +192,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() {
|
||||
// Bob is able to decrypt it using the bundle
|
||||
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
s.Equal(cleartext, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
|
||||
}
|
||||
|
||||
// Alice has Bob's bundle
|
||||
@ -267,7 +258,7 @@ func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() {
|
||||
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
s.Equal(cleartext2, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
|
||||
}
|
||||
|
||||
// Alice has Bob's bundle
|
||||
@ -351,7 +342,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
|
||||
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
s.Equal(cleartext2, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
|
||||
}
|
||||
|
||||
// Previous implementation allowed max maxSkip keys in the same receiving chain
|
||||
@ -686,7 +677,7 @@ func receiver(
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(actualCleartext, cleartext) {
|
||||
if !reflect.DeepEqual(actualCleartext.DecryptedMessage, cleartext) {
|
||||
errChan <- errors.New("Decrypted value does not match")
|
||||
return
|
||||
}
|
||||
|
@ -38,7 +38,9 @@ type ProtocolMessageSpec struct {
|
||||
// Installations is the targeted devices
|
||||
Installations []*multidevice.Installation
|
||||
// SharedSecret is a shared secret established among the installations
|
||||
SharedSecret []byte
|
||||
SharedSecret *sharedsecret.Secret
|
||||
// AgreedSecret indicates whether the shared secret has been agreed
|
||||
AgreedSecret bool
|
||||
// Public means that the spec contains a public wrapped message
|
||||
Public bool
|
||||
}
|
||||
@ -66,14 +68,11 @@ func (p *ProtocolMessageSpec) PartitionedTopicMode() PartitionTopicMode {
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
encryptor *encryptor
|
||||
secret *sharedsecret.SharedSecret
|
||||
multidevice *multidevice.Multidevice
|
||||
publisher *publisher.Publisher
|
||||
|
||||
onAddedBundlesHandler func([]*multidevice.Installation)
|
||||
onNewSharedSecretHandler func([]*sharedsecret.Secret)
|
||||
onSendContactCodeHandler func(*ProtocolMessageSpec)
|
||||
encryptor *encryptor
|
||||
secret *sharedsecret.SharedSecret
|
||||
multidevice *multidevice.Multidevice
|
||||
publisher *publisher.Publisher
|
||||
subscriptions *Subscriptions
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
@ -87,18 +86,12 @@ var (
|
||||
func New(
|
||||
db *sql.DB,
|
||||
installationID string,
|
||||
addedBundlesHandler func([]*multidevice.Installation),
|
||||
onNewSharedSecretHandler func([]*sharedsecret.Secret),
|
||||
onSendContactCodeHandler func(*ProtocolMessageSpec),
|
||||
logger *zap.Logger,
|
||||
) *Protocol {
|
||||
return NewWithEncryptorConfig(
|
||||
db,
|
||||
installationID,
|
||||
defaultEncryptorConfig(installationID, logger),
|
||||
addedBundlesHandler,
|
||||
onNewSharedSecretHandler,
|
||||
onSendContactCodeHandler,
|
||||
logger,
|
||||
)
|
||||
}
|
||||
@ -109,9 +102,6 @@ func NewWithEncryptorConfig(
|
||||
db *sql.DB,
|
||||
installationID string,
|
||||
encryptorConfig encryptorConfig,
|
||||
addedBundlesHandler func([]*multidevice.Installation),
|
||||
onNewSharedSecretHandler func([]*sharedsecret.Secret),
|
||||
onSendContactCodeHandler func(*ProtocolMessageSpec),
|
||||
logger *zap.Logger,
|
||||
) *Protocol {
|
||||
return &Protocol{
|
||||
@ -122,39 +112,36 @@ func NewWithEncryptorConfig(
|
||||
ProtocolVersion: protocolVersion,
|
||||
InstallationID: installationID,
|
||||
}),
|
||||
publisher: publisher.New(logger),
|
||||
onAddedBundlesHandler: addedBundlesHandler,
|
||||
onNewSharedSecretHandler: onNewSharedSecretHandler,
|
||||
onSendContactCodeHandler: onSendContactCodeHandler,
|
||||
logger: logger.With(zap.Namespace("Protocol")),
|
||||
publisher: publisher.New(logger),
|
||||
logger: logger.With(zap.Namespace("Protocol")),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) error {
|
||||
type Subscriptions struct {
|
||||
SharedSecrets []*sharedsecret.Secret
|
||||
SendContactCode <-chan struct{}
|
||||
Quit chan struct{}
|
||||
}
|
||||
|
||||
func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) (*Subscriptions, error) {
|
||||
// Propagate currently cached shared secrets.
|
||||
secrets, err := p.secret.All()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get all secrets")
|
||||
return nil, errors.Wrap(err, "failed to get all secrets")
|
||||
}
|
||||
p.onNewSharedSecretHandler(secrets)
|
||||
|
||||
// Handle Publisher system messages.
|
||||
publisherCh := p.publisher.Start()
|
||||
|
||||
go func() {
|
||||
for range publisherCh {
|
||||
messageSpec, err := p.buildContactCodeMessage(myIdentity)
|
||||
if err != nil {
|
||||
p.logger.Error("failed to build contact code message",
|
||||
zap.String("site", "Start"),
|
||||
zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
p.onSendContactCodeHandler(messageSpec)
|
||||
}
|
||||
}()
|
||||
p.subscriptions = &Subscriptions{
|
||||
SharedSecrets: secrets,
|
||||
SendContactCode: p.publisher.Start(),
|
||||
Quit: make(chan struct{}),
|
||||
}
|
||||
return p.subscriptions, nil
|
||||
}
|
||||
|
||||
func (p *Protocol) Stop() error {
|
||||
p.publisher.Stop()
|
||||
if p.subscriptions != nil {
|
||||
close(p.subscriptions.Quit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -197,12 +184,6 @@ func (p *Protocol) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload [
|
||||
return &ProtocolMessageSpec{Message: message, Public: true}, nil
|
||||
}
|
||||
|
||||
// buildContactCodeMessage creates a contact code message. It's a public message
|
||||
// without any data but it carries bundle information.
|
||||
func (p *Protocol) buildContactCodeMessage(myIdentityKey *ecdsa.PrivateKey) (*ProtocolMessageSpec, error) {
|
||||
return p.BuildPublicMessage(myIdentityKey, nil)
|
||||
}
|
||||
|
||||
// BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload
|
||||
func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
logger := p.logger.With(
|
||||
@ -253,18 +234,12 @@ func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey
|
||||
zap.Bool("has-shared-secret", sharedSecret != nil),
|
||||
zap.Bool("agreed", agreed))
|
||||
|
||||
// Call handler
|
||||
if sharedSecret != nil {
|
||||
p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret})
|
||||
}
|
||||
|
||||
spec := &ProtocolMessageSpec{
|
||||
SharedSecret: sharedSecret,
|
||||
AgreedSecret: agreed,
|
||||
Message: message,
|
||||
Installations: installations,
|
||||
}
|
||||
if agreed {
|
||||
spec.SharedSecret = sharedSecret.Key
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
@ -410,14 +385,21 @@ func (p *Protocol) ConfirmMessageProcessed(messageID []byte) error {
|
||||
return p.encryptor.ConfirmMessageProcessed(messageID)
|
||||
}
|
||||
|
||||
type DecryptMessageResponse struct {
|
||||
DecryptedMessage []byte
|
||||
Installations []*multidevice.Installation
|
||||
SharedSecrets []*sharedsecret.Secret
|
||||
}
|
||||
|
||||
// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message.
|
||||
func (p *Protocol) HandleMessage(
|
||||
myIdentityKey *ecdsa.PrivateKey,
|
||||
theirPublicKey *ecdsa.PublicKey,
|
||||
protocolMessage *ProtocolMessage,
|
||||
messageID []byte,
|
||||
) ([]byte, error) {
|
||||
) (*DecryptMessageResponse, error) {
|
||||
logger := p.logger.With(zap.String("site", "HandleMessage"))
|
||||
response := &DecryptMessageResponse{}
|
||||
|
||||
logger.Debug("received a protocol message", zap.Binary("sender-public-key", crypto.FromECDSAPub(theirPublicKey)), zap.Binary("message-id", messageID))
|
||||
|
||||
@ -428,19 +410,19 @@ func (p *Protocol) HandleMessage(
|
||||
// Process bundles
|
||||
for _, bundle := range protocolMessage.GetBundles() {
|
||||
// Should we stop processing if the bundle cannot be verified?
|
||||
addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
newInstallations, err := p.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.onAddedBundlesHandler(addedBundles)
|
||||
response.Installations = newInstallations
|
||||
}
|
||||
|
||||
// Check if it's a public message
|
||||
if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil {
|
||||
logger.Debug("received a public message in direct message")
|
||||
// Nothing to do, as already in cleartext
|
||||
return publicMessage, nil
|
||||
response.DecryptedMessage = publicMessage
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Decrypt message
|
||||
@ -468,9 +450,10 @@ func (p *Protocol) HandleMessage(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret})
|
||||
response.SharedSecrets = []*sharedsecret.Secret{sharedSecret}
|
||||
}
|
||||
return message, nil
|
||||
response.DecryptedMessage = message
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Return error
|
||||
|
@ -13,9 +13,6 @@ import (
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
)
|
||||
|
||||
func TestProtocolServiceTestSuite(t *testing.T) {
|
||||
@ -44,17 +41,11 @@ func (s *ProtocolServiceTestSuite) SetupTest() {
|
||||
s.Require().NoError(err)
|
||||
bobDBKey := "bob"
|
||||
|
||||
addedBundlesHandler := func(addedBundles []*multidevice.Installation) {}
|
||||
onNewSharedSecretHandler := func(secret []*sharedsecret.Secret) {}
|
||||
|
||||
db, err := sqlite.Open(s.aliceDBPath.Name(), aliceDBKey)
|
||||
s.Require().NoError(err)
|
||||
s.alice = New(
|
||||
db,
|
||||
"1",
|
||||
addedBundlesHandler,
|
||||
onNewSharedSecretHandler,
|
||||
func(*ProtocolMessageSpec) {},
|
||||
s.logger.With(zap.String("user", "alice")),
|
||||
)
|
||||
|
||||
@ -63,9 +54,6 @@ func (s *ProtocolServiceTestSuite) SetupTest() {
|
||||
s.bob = New(
|
||||
db,
|
||||
"2",
|
||||
addedBundlesHandler,
|
||||
onNewSharedSecretHandler,
|
||||
func(*ProtocolMessageSpec) {},
|
||||
s.logger.With(zap.String("user", "bob")),
|
||||
)
|
||||
}
|
||||
@ -142,7 +130,6 @@ func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
|
||||
}
|
||||
|
||||
func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
|
||||
var secretResponse []*sharedsecret.Secret
|
||||
bobKey, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
aliceKey, err := crypto.GenerateKey()
|
||||
@ -150,12 +137,13 @@ func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
|
||||
|
||||
payload := []byte("test")
|
||||
|
||||
s.bob.onNewSharedSecretHandler = func(secret []*sharedsecret.Secret) {
|
||||
secretResponse = secret
|
||||
}
|
||||
_, err = s.bob.Start(bobKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
|
||||
s.NoError(err)
|
||||
s.NotNil(msgSpec, "It creates a message spec")
|
||||
s.Require().NotNil(msgSpec.SharedSecret)
|
||||
|
||||
bundle := msgSpec.Message.GetBundles()[0]
|
||||
s.Require().NotNil(bundle)
|
||||
@ -171,12 +159,10 @@ func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
|
||||
_, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, msgSpec.Message, []byte("message-id"))
|
||||
s.NoError(err)
|
||||
|
||||
s.Require().NotNil(secretResponse)
|
||||
s.Require().NoError(s.bob.Stop())
|
||||
}
|
||||
|
||||
func (s *ProtocolServiceTestSuite) TestPropagatingSavedSharedSecretsOnStart() {
|
||||
var secretResponse []*sharedsecret.Secret
|
||||
|
||||
aliceKey, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
bobKey, err := crypto.GenerateKey()
|
||||
@ -186,15 +172,14 @@ func (s *ProtocolServiceTestSuite) TestPropagatingSavedSharedSecretsOnStart() {
|
||||
generatedSecret, err := s.alice.secret.Generate(aliceKey, &bobKey.PublicKey, "installation-1")
|
||||
s.NoError(err)
|
||||
|
||||
s.alice.onNewSharedSecretHandler = func(secret []*sharedsecret.Secret) {
|
||||
secretResponse = secret
|
||||
}
|
||||
subscriptions, err := s.alice.Start(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.alice.Start(aliceKey)
|
||||
s.NoError(err)
|
||||
secretResponse := subscriptions.SharedSecrets
|
||||
|
||||
s.Require().NotNil(secretResponse)
|
||||
s.Require().Len(secretResponse, 1)
|
||||
s.Equal(crypto.FromECDSAPub(generatedSecret.Identity), crypto.FromECDSAPub(secretResponse[0].Identity))
|
||||
s.Equal(generatedSecret.Key, secretResponse[0].Key)
|
||||
s.Require().NoError(s.alice.Stop())
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ func (p *Publisher) Start() <-chan struct{} {
|
||||
|
||||
logger.Info("starting publisher")
|
||||
|
||||
p.notifyCh = make(chan struct{})
|
||||
p.notifyCh = make(chan struct{}, 100)
|
||||
p.quit = make(chan struct{})
|
||||
|
||||
go p.tickerLoop()
|
||||
@ -55,6 +55,10 @@ func (p *Publisher) Start() <-chan struct{} {
|
||||
}
|
||||
|
||||
func (p *Publisher) Stop() {
|
||||
// If hasn't started, ignore
|
||||
if p.quit == nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case _, ok := <-p.quit:
|
||||
if !ok {
|
||||
@ -101,7 +105,11 @@ func (p *Publisher) notify() error {
|
||||
return errNotEnoughTimePassed
|
||||
}
|
||||
|
||||
p.notifyCh <- struct{}{}
|
||||
select {
|
||||
case p.notifyCh <- struct{}{}:
|
||||
default:
|
||||
p.logger.Warn("publisher channel full, dropping message")
|
||||
}
|
||||
|
||||
p.persistence.setLastPublished(now)
|
||||
return nil
|
||||
|
@ -56,6 +56,7 @@ var (
|
||||
// mailservers because they can also be managed by the user.
|
||||
type Messenger struct {
|
||||
node types.Node
|
||||
config *config
|
||||
identity *ecdsa.PrivateKey
|
||||
persistence *sqlitePersistence
|
||||
transport transport.Transport
|
||||
@ -127,50 +128,6 @@ func NewMessenger(
|
||||
}
|
||||
}
|
||||
|
||||
onNewInstallationsHandler := func(installations []*multidevice.Installation) {
|
||||
|
||||
for _, installation := range installations {
|
||||
if installation.Identity == contactIDFromPublicKey(&messenger.identity.PublicKey) {
|
||||
if _, ok := messenger.allInstallations[installation.ID]; !ok {
|
||||
messenger.allInstallations[installation.ID] = installation
|
||||
messenger.modifiedInstallations[installation.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set default config fields.
|
||||
onNewSharedSecretHandler := func(secrets []*sharedsecret.Secret) {
|
||||
filters, err := messenger.handleSharedSecrets(secrets)
|
||||
if err != nil {
|
||||
slogger := logger.With(zap.String("site", "onNewSharedSecretHandler"))
|
||||
slogger.Warn("failed to process secrets", zap.Error(err))
|
||||
}
|
||||
|
||||
if c.onNegotiatedFilters != nil {
|
||||
c.onNegotiatedFilters(filters)
|
||||
}
|
||||
}
|
||||
if c.onSendContactCodeHandler == nil {
|
||||
c.onSendContactCodeHandler = func(messageSpec *encryption.ProtocolMessageSpec) {
|
||||
slogger := logger.With(zap.String("site", "onSendContactCodeHandler"))
|
||||
slogger.Debug("received a SendContactCode request")
|
||||
|
||||
newMessage, err := common.MessageSpecToWhisper(messageSpec)
|
||||
if err != nil {
|
||||
slogger.Warn("failed to convert spec to Whisper message", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
chatName := transport.ContactCodeTopic(&messenger.identity.PublicKey)
|
||||
_, err = messenger.transport.SendPublic(ctx, newMessage, chatName)
|
||||
if err != nil {
|
||||
slogger.Warn("failed to send a contact code", zap.Error(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.systemMessagesTranslations == nil {
|
||||
c.systemMessagesTranslations = defaultSystemMessagesTranslations
|
||||
}
|
||||
@ -232,9 +189,6 @@ func NewMessenger(
|
||||
encryptionProtocol := encryption.New(
|
||||
database,
|
||||
installationID,
|
||||
onNewInstallationsHandler,
|
||||
onNewSharedSecretHandler,
|
||||
c.onSendContactCodeHandler,
|
||||
logger,
|
||||
)
|
||||
|
||||
@ -267,8 +221,6 @@ func NewMessenger(
|
||||
|
||||
// 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
|
||||
|
||||
@ -277,6 +229,7 @@ func NewMessenger(
|
||||
handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
|
||||
|
||||
messenger = &Messenger{
|
||||
config: &c,
|
||||
node: node,
|
||||
identity: identity,
|
||||
persistence: &sqlitePersistence{db: database},
|
||||
@ -299,6 +252,7 @@ func NewMessenger(
|
||||
shutdownTasks: []func() error{
|
||||
database.Close,
|
||||
pushNotificationClient.Stop,
|
||||
encryptionProtocol.Stop,
|
||||
transp.ResetFilters,
|
||||
transp.Stop,
|
||||
func() error { processor.Stop(); return nil },
|
||||
@ -325,12 +279,147 @@ func (m *Messenger) Start() error {
|
||||
|
||||
// Start push notification client
|
||||
if m.pushNotificationClient != nil {
|
||||
m.handlePushNotificationClientRegistrations(m.pushNotificationClient.SubscribeToRegistrations())
|
||||
|
||||
if err := m.pushNotificationClient.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.encryptor.Start(m.identity)
|
||||
// set shared secret handles
|
||||
m.processor.SetHandleSharedSecrets(m.handleSharedSecrets)
|
||||
|
||||
subscriptions, err := m.encryptor.Start(m.identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// handle stored shared secrets
|
||||
err = m.handleSharedSecrets(subscriptions.SharedSecrets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.handleEncryptionLayerSubscriptions(subscriptions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) buildContactCodeAdvertisement() (*protobuf.ContactCodeAdvertisement, error) {
|
||||
if m.pushNotificationClient == nil || !m.pushNotificationClient.Enabled() {
|
||||
return nil, nil
|
||||
}
|
||||
m.logger.Debug("adding push notification info to contact code bundle")
|
||||
info, err := m.pushNotificationClient.MyPushNotificationQueryInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(info) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &protobuf.ContactCodeAdvertisement{
|
||||
PushNotificationInfo: info,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// handleSendContactCode sends a public message wrapped in the encryption
|
||||
// layer, which will propagate our bundle
|
||||
func (m *Messenger) handleSendContactCode() error {
|
||||
var payload []byte
|
||||
m.logger.Debug("sending contact code")
|
||||
contactCodeAdvertisement, err := m.buildContactCodeAdvertisement()
|
||||
if err != nil {
|
||||
m.logger.Error("could not build contact code advertisement", zap.Error(err))
|
||||
}
|
||||
|
||||
if contactCodeAdvertisement != nil {
|
||||
payload, err = proto.Marshal(contactCodeAdvertisement)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: contactCodeTopic,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT,
|
||||
Payload: payload,
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_, err = m.processor.SendPublic(ctx, contactCodeTopic, rawMessage)
|
||||
if err != nil {
|
||||
m.logger.Warn("failed to send a contact code", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// handleSharedSecrets process the negotiated secrets received from the encryption layer
|
||||
func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error {
|
||||
logger := m.logger.With(zap.String("site", "handleSharedSecrets"))
|
||||
var result []*transport.Filter
|
||||
for _, secret := range secrets {
|
||||
logger.Debug("received shared secret", zap.Binary("identity", crypto.FromECDSAPub(secret.Identity)))
|
||||
fSecret := types.NegotiatedSecret{
|
||||
PublicKey: secret.Identity,
|
||||
Key: secret.Key,
|
||||
}
|
||||
filter, err := m.transport.ProcessNegotiatedSecret(fSecret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result = append(result, filter)
|
||||
}
|
||||
if m.config.onNegotiatedFilters != nil {
|
||||
m.config.onNegotiatedFilters(result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleInstallations adds the installations in the installations map
|
||||
func (m *Messenger) handleInstallations(installations []*multidevice.Installation) {
|
||||
for _, installation := range installations {
|
||||
if installation.Identity == contactIDFromPublicKey(&m.identity.PublicKey) {
|
||||
if _, ok := m.allInstallations[installation.ID]; !ok {
|
||||
m.allInstallations[installation.ID] = installation
|
||||
m.modifiedInstallations[installation.ID] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleEncryptionLayerSubscriptions handles events from the encryption layer
|
||||
func (m *Messenger) handleEncryptionLayerSubscriptions(subscriptions *encryption.Subscriptions) {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-subscriptions.SendContactCode:
|
||||
if err := m.handleSendContactCode(); err != nil {
|
||||
m.logger.Error("failed to publish contact code", zap.Error(err))
|
||||
}
|
||||
|
||||
case <-subscriptions.Quit:
|
||||
m.logger.Debug("quitting encryption subscription loop")
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// handlePushNotificationClientRegistration handles registration events
|
||||
func (m *Messenger) handlePushNotificationClientRegistrations(c chan struct{}) {
|
||||
go func() {
|
||||
for {
|
||||
_, more := <-c
|
||||
if !more {
|
||||
return
|
||||
}
|
||||
if err := m.handleSendContactCode(); err != nil {
|
||||
m.logger.Error("failed to publish contact code", zap.Error(err))
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Init analyzes chats and contacts in order to setup filters
|
||||
@ -436,24 +525,6 @@ func (m *Messenger) Shutdown() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) ([]*transport.Filter, error) {
|
||||
logger := m.logger.With(zap.String("site", "handleSharedSecrets"))
|
||||
var result []*transport.Filter
|
||||
for _, secret := range secrets {
|
||||
logger.Debug("received shared secret", zap.Binary("identity", crypto.FromECDSAPub(secret.Identity)))
|
||||
fSecret := types.NegotiatedSecret{
|
||||
PublicKey: secret.Identity,
|
||||
Key: secret.Key,
|
||||
}
|
||||
filter, err := m.transport.ProcessNegotiatedSecret(fSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, filter)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) EnableInstallation(id string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
@ -1152,7 +1223,7 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
||||
}
|
||||
|
||||
// We check if it should re-register with the push notification server
|
||||
shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact))
|
||||
shouldReregisterForPushNotifications := (m.isNewContact(contact) || m.removedContact(contact))
|
||||
|
||||
err = m.persistence.SaveContact(contact, nil)
|
||||
if err != nil {
|
||||
@ -1163,17 +1234,22 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
||||
|
||||
// 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 m.reregisterForPushNotifications()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) reregisterForPushNotifications() error {
|
||||
m.logger.Info("contact state changed, re-registering for push notification")
|
||||
if m.pushNotificationClient == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
return m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
|
||||
}
|
||||
|
||||
func (m *Messenger) SaveContact(contact *Contact) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
@ -1816,6 +1892,13 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
||||
for _, msg := range statusMessages {
|
||||
publicKey := msg.SigPubKey()
|
||||
|
||||
m.handleInstallations(msg.Installations)
|
||||
err := m.handleSharedSecrets(msg.SharedSecrets)
|
||||
if err != nil {
|
||||
// log and continue, non-critical error
|
||||
logger.Warn("failed to handle shared secrets")
|
||||
}
|
||||
|
||||
// Check for messages from blocked users
|
||||
senderID := contactIDFromPublicKey(publicKey)
|
||||
if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() {
|
||||
@ -1828,6 +1911,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
||||
logger.Warn("failed to check message exists", zap.Error(err))
|
||||
}
|
||||
if exists {
|
||||
logger.Debug("messageExists", zap.String("messageID", messageID))
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1999,6 +2083,18 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
case protobuf.ContactCodeAdvertisement:
|
||||
logger.Debug("Received ContactCodeAdvertisement")
|
||||
if m.pushNotificationClient == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling ContactCodeAdvertisement")
|
||||
if err := m.pushNotificationClient.HandleContactCodeAdvertisement(publicKey, msg.ParsedMessage.Interface().(protobuf.ContactCodeAdvertisement)); err != nil {
|
||||
logger.Warn("failed to handle ContactCodeAdvertisement", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
|
||||
case protobuf.PushNotificationResponse:
|
||||
logger.Debug("Received PushNotificationResponse")
|
||||
if m.pushNotificationClient == nil {
|
||||
@ -2060,6 +2156,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
||||
logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage.Interface())))
|
||||
|
||||
}
|
||||
} else {
|
||||
logger.Debug("parsed message is nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2251,7 +2349,8 @@ func (m *Messenger) MuteChat(chatID string) error {
|
||||
|
||||
chat.Muted = true
|
||||
m.allChats[chat.ID] = chat
|
||||
return nil
|
||||
|
||||
return m.reregisterForPushNotifications()
|
||||
}
|
||||
|
||||
// UnmuteChat signals to the messenger that we want to be notified
|
||||
@ -2271,7 +2370,7 @@ func (m *Messenger) UnmuteChat(chatID string) error {
|
||||
|
||||
chat.Muted = false
|
||||
m.allChats[chat.ID] = chat
|
||||
return nil
|
||||
return m.reregisterForPushNotifications()
|
||||
}
|
||||
|
||||
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
||||
@ -3146,7 +3245,7 @@ func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string
|
||||
}
|
||||
|
||||
// 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, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
@ -3154,7 +3253,12 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs)
|
||||
err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, contactIDs, mutedChatIDs)
|
||||
if err != nil {
|
||||
m.logger.Error("failed to register for push notifications", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisteredForPushNotifications returns whether we successfully registered with all the servers
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"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/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
@ -18,8 +17,6 @@ type config struct {
|
||||
// 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
|
||||
|
@ -44,6 +44,11 @@ func (s *MessengerContactUpdateSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerContactUpdateSuite) TearDownTest() {
|
||||
s.Require().NoError(s.m.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
@ -85,6 +90,7 @@ func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() {
|
||||
contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirContactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
response, err := theirMessenger.SendContactUpdate(context.Background(), contactID, theirName, theirPicture)
|
||||
@ -136,4 +142,5 @@ func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() {
|
||||
s.Require().False(receivedContact.ENSVerified)
|
||||
s.Require().True(receivedContact.HasBeenAdded())
|
||||
s.Require().NotEmpty(receivedContact.LastUpdated)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
@ -47,6 +47,11 @@ func (s *MessengerEmojiSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerEmojiSuite) TearDownTest() {
|
||||
s.Require().NoError(s.m.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerEmojiSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
@ -89,6 +94,7 @@ func (s *MessengerEmojiSuite) TestSendEmoji() {
|
||||
s.Require().NoError(err)
|
||||
|
||||
bob := s.newMessengerWithKey(s.shh, key)
|
||||
s.Require().NoError(bob.Start())
|
||||
|
||||
chatID := statusChatID
|
||||
|
||||
@ -162,11 +168,13 @@ func (s *MessengerEmojiSuite) TestSendEmoji() {
|
||||
s.Require().Equal(response.EmojiReactions[0].ID(), emojiID)
|
||||
s.Require().Equal(response.EmojiReactions[0].Type, protobuf.EmojiReaction_SAD)
|
||||
s.Require().True(response.EmojiReactions[0].Retracted)
|
||||
s.Require().NoError(bob.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerEmojiSuite) TestEmojiPrivateGroup() {
|
||||
bob := s.m
|
||||
alice := s.newMessenger(s.shh)
|
||||
s.Require().NoError(alice.Start())
|
||||
response, err := bob.CreateGroupChatWithMembers(context.Background(), "test", []string{})
|
||||
s.NoError(err)
|
||||
|
||||
@ -217,4 +225,5 @@ func (s *MessengerEmojiSuite) TestEmojiPrivateGroup() {
|
||||
"no emoji reaction received",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
}
|
||||
|
@ -50,6 +50,12 @@ func (s *MessengerInstallationSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
// We start the messenger in order to receive installations
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) TearDownTest() {
|
||||
s.Require().NoError(s.m.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
@ -88,6 +94,7 @@ func (s *MessengerInstallationSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
|
||||
func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
||||
theirMessenger := s.newMessengerWithKey(s.shh, s.privateKey)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
|
||||
err := theirMessenger.SetInstallationMetadata(theirMessenger.installationID, &multidevice.InstallationMetadata{
|
||||
Name: "their-name",
|
||||
@ -153,6 +160,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
||||
actualChat := response.Chats[0]
|
||||
s.Require().Equal(statusChatID, actualChat.ID)
|
||||
s.Require().True(actualChat.Active)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
||||
@ -174,6 +182,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
||||
|
||||
// pair
|
||||
theirMessenger := s.newMessengerWithKey(s.shh, s.privateKey)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
|
||||
err = theirMessenger.SetInstallationMetadata(theirMessenger.installationID, &multidevice.InstallationMetadata{
|
||||
Name: "their-name",
|
||||
@ -240,6 +249,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
||||
s.Require().NotNil(statusChat)
|
||||
|
||||
s.Require().True(actualContact.IsAdded())
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
|
||||
@ -247,7 +257,9 @@ func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
|
||||
bob1 := s.m
|
||||
// pair
|
||||
bob2 := s.newMessengerWithKey(s.shh, s.privateKey)
|
||||
s.Require().NoError(bob2.Start())
|
||||
alice := s.newMessenger(s.shh)
|
||||
s.Require().NoError(alice.Start())
|
||||
|
||||
err := bob2.SetInstallationMetadata(bob2.installationID, &multidevice.InstallationMetadata{
|
||||
Name: "their-name",
|
||||
@ -290,4 +302,6 @@ func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
|
||||
"message not received",
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(bob2.Shutdown())
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
}
|
||||
|
@ -45,6 +45,11 @@ func (s *MessengerMuteSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) TearDownTest() {
|
||||
s.Require().NoError(s.m.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
@ -86,6 +91,7 @@ func (s *MessengerMuteSuite) TestSetMute() {
|
||||
s.Require().NoError(err)
|
||||
|
||||
theirMessenger := s.newMessengerWithKey(s.shh, key)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
|
||||
chatID := "status"
|
||||
|
||||
@ -107,4 +113,5 @@ func (s *MessengerMuteSuite) TestSetMute() {
|
||||
|
||||
s.Require().NoError(s.m.UnmuteChat(chatID))
|
||||
s.Require().False(s.m.Chats()[0].Muted)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ func (s *MessengerSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
@ -503,6 +504,7 @@ func (s *MessengerSuite) TestRetrieveOwnPublic() {
|
||||
// Retrieve their public message
|
||||
func (s *MessengerSuite) TestRetrieveTheirPublic() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirChat := CreatePublicChat("status", s.m.transport)
|
||||
err := theirMessenger.SaveChat(&theirChat)
|
||||
s.Require().NoError(err)
|
||||
@ -539,10 +541,12 @@ func (s *MessengerSuite) TestRetrieveTheirPublic() {
|
||||
s.Require().Equal(sentMessage.Clock, actualChat.LastClockValue)
|
||||
// It sets the last message
|
||||
s.Require().NotNil(actualChat.LastMessage)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestDeletedAtClockValue() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirChat := CreatePublicChat("status", s.m.transport)
|
||||
err := theirMessenger.SaveChat(&theirChat)
|
||||
s.Require().NoError(err)
|
||||
@ -568,10 +572,12 @@ func (s *MessengerSuite) TestDeletedAtClockValue() {
|
||||
response, err := s.m.RetrieveAll()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(response.Messages, 0)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestRetrieveBlockedContact() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirChat := CreatePublicChat("status", s.m.transport)
|
||||
err := theirMessenger.SaveChat(&theirChat)
|
||||
s.Require().NoError(err)
|
||||
@ -610,6 +616,7 @@ func (s *MessengerSuite) TestRetrieveBlockedContact() {
|
||||
// Resend their public message, receive only once
|
||||
func (s *MessengerSuite) TestResendPublicMessage() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirChat := CreatePublicChat("status", s.m.transport)
|
||||
err := theirMessenger.SaveChat(&theirChat)
|
||||
s.Require().NoError(err)
|
||||
@ -663,6 +670,7 @@ func (s *MessengerSuite) TestResendPublicMessage() {
|
||||
// Test receiving a message on an existing private chat
|
||||
func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirChat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey, s.m.transport)
|
||||
err := theirMessenger.SaveChat(&theirChat)
|
||||
s.Require().NoError(err)
|
||||
@ -698,11 +706,13 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() {
|
||||
// It sets the last message
|
||||
s.Require().NotNil(actualChat.LastMessage)
|
||||
s.Require().True(actualChat.Active)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
// Test receiving a message on an non-existing private chat
|
||||
func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
chat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey, s.m.transport)
|
||||
err := theirMessenger.SaveChat(&chat)
|
||||
s.NoError(err)
|
||||
@ -739,6 +749,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() {
|
||||
// Test receiving a message on an non-existing public chat
|
||||
func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() {
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
chat := CreatePublicChat("test-chat", s.m.transport)
|
||||
err := theirMessenger.SaveChat(&chat)
|
||||
s.NoError(err)
|
||||
@ -756,12 +767,14 @@ func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() {
|
||||
|
||||
s.Require().Equal(len(response.Messages), 0)
|
||||
s.Require().Equal(len(response.Chats), 0)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
// Test receiving a message on an existing private group chat
|
||||
func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() {
|
||||
var response *MessengerResponse
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "id", []string{})
|
||||
s.NoError(err)
|
||||
s.Require().Len(response.Chats, 1)
|
||||
@ -823,6 +836,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() {
|
||||
func (s *MessengerSuite) TestChangeNameGroupChat() {
|
||||
var response *MessengerResponse
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "old-name", []string{})
|
||||
s.NoError(err)
|
||||
s.Require().Len(response.Chats, 1)
|
||||
@ -869,12 +883,14 @@ func (s *MessengerSuite) TestChangeNameGroupChat() {
|
||||
s.Require().Len(response.Chats, 1)
|
||||
actualChat := response.Chats[0]
|
||||
s.Require().Equal(newName, actualChat.Name)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
// Test being re-invited to a group chat
|
||||
func (s *MessengerSuite) TestReInvitedToGroupChat() {
|
||||
var response *MessengerResponse
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "old-name", []string{})
|
||||
s.NoError(err)
|
||||
s.Require().Len(response.Chats, 1)
|
||||
@ -928,6 +944,7 @@ func (s *MessengerSuite) TestReInvitedToGroupChat() {
|
||||
|
||||
s.Require().Len(response.Chats, 1)
|
||||
s.Require().True(response.Chats[0].Active)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestChatPersistencePublic() {
|
||||
@ -1388,7 +1405,7 @@ func (s *MessengerSuite) TestContactPersistenceUpdate() {
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestSharedSecretHandler() {
|
||||
_, err := s.m.handleSharedSecrets(nil)
|
||||
err := s.m.handleSharedSecrets(nil)
|
||||
s.NoError(err)
|
||||
}
|
||||
|
||||
@ -1434,6 +1451,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
|
||||
value := testValue
|
||||
contract := testContract
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
|
||||
@ -1516,6 +1534,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
|
||||
s.Require().Equal(CommandStateRequestAddressForTransactionDeclined, receiverMessage.CommandParameters.CommandState)
|
||||
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
|
||||
s.Require().Equal(initialCommandID, receiverMessage.Replace)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestSendEthTransaction() {
|
||||
@ -1523,6 +1542,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
|
||||
contract := testContract
|
||||
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
receiverAddress := crypto.PubkeyToAddress(theirMessenger.identity.PublicKey)
|
||||
@ -1617,6 +1637,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
|
||||
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
|
||||
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
|
||||
s.Require().Equal("", receiverMessage.Replace)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestSendTokenTransaction() {
|
||||
@ -1624,6 +1645,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
|
||||
contract := testContract
|
||||
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
receiverAddress := crypto.PubkeyToAddress(theirMessenger.identity.PublicKey)
|
||||
@ -1718,12 +1740,14 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
|
||||
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
|
||||
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
|
||||
s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
|
||||
value := testValue
|
||||
contract := testContract
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
myAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
|
||||
@ -1808,6 +1832,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
|
||||
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
|
||||
s.Require().Equal("some-address", receiverMessage.CommandParameters.Address)
|
||||
s.Require().Equal(initialCommandID, receiverMessage.Replace)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestDeclineRequestTransaction() {
|
||||
@ -1816,6 +1841,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
|
||||
receiverAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
|
||||
receiverAddressString := strings.ToLower(receiverAddress.Hex())
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
|
||||
@ -1895,6 +1921,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
|
||||
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
|
||||
s.Require().Equal(initialCommandID, receiverMessage.Replace)
|
||||
s.Require().Equal(CommandStateRequestTransactionDeclined, receiverMessage.CommandParameters.CommandState)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) TestRequestTransaction() {
|
||||
@ -1903,6 +1930,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
|
||||
receiverAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
|
||||
receiverAddressString := strings.ToLower(receiverAddress.Hex())
|
||||
theirMessenger := s.newMessenger(s.shh)
|
||||
s.Require().NoError(theirMessenger.Start())
|
||||
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
|
||||
|
||||
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
|
||||
@ -2039,6 +2067,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
|
||||
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
|
||||
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
|
||||
s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
|
||||
s.Require().NoError(theirMessenger.Shutdown())
|
||||
}
|
||||
|
||||
type MockTransaction struct {
|
||||
|
@ -82,6 +82,34 @@ func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte,
|
||||
return fileDescriptor_200acd86044eaa5d, []int{1, 0}
|
||||
}
|
||||
|
||||
type PushNotification_PushNotificationType int32
|
||||
|
||||
const (
|
||||
PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE PushNotification_PushNotificationType = 0
|
||||
PushNotification_MESSAGE PushNotification_PushNotificationType = 1
|
||||
PushNotification_MENTION PushNotification_PushNotificationType = 2
|
||||
)
|
||||
|
||||
var PushNotification_PushNotificationType_name = map[int32]string{
|
||||
0: "UNKNOWN_PUSH_NOTIFICATION_TYPE",
|
||||
1: "MESSAGE",
|
||||
2: "MENTION",
|
||||
}
|
||||
|
||||
var PushNotification_PushNotificationType_value = map[string]int32{
|
||||
"UNKNOWN_PUSH_NOTIFICATION_TYPE": 0,
|
||||
"MESSAGE": 1,
|
||||
"MENTION": 2,
|
||||
}
|
||||
|
||||
func (x PushNotification_PushNotificationType) String() string {
|
||||
return proto.EnumName(PushNotification_PushNotificationType_name, int32(x))
|
||||
}
|
||||
|
||||
func (PushNotification_PushNotificationType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{6, 0}
|
||||
}
|
||||
|
||||
type PushNotificationReport_ErrorType int32
|
||||
|
||||
const (
|
||||
@ -125,6 +153,7 @@ type PushNotificationRegistration struct {
|
||||
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"`
|
||||
ApnTopic string `protobuf:"bytes,12,opt,name=apn_topic,json=apnTopic,proto3" json:"apn_topic,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@ -232,6 +261,13 @@ func (m *PushNotificationRegistration) GetAllowFromContactsOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetApnTopic() string {
|
||||
if m != nil {
|
||||
return m.ApnTopic
|
||||
}
|
||||
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"`
|
||||
@ -508,14 +544,15 @@ 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:"-"`
|
||||
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"`
|
||||
Type PushNotification_PushNotificationType `protobuf:"varint,6,opt,name=type,proto3,enum=protobuf.PushNotification_PushNotificationType" json:"type,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotification) Reset() { *m = PushNotification{} }
|
||||
@ -578,6 +615,13 @@ func (m *PushNotification) GetMessage() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotification) GetType() PushNotification_PushNotificationType {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE
|
||||
}
|
||||
|
||||
type PushNotificationRequest struct {
|
||||
Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"`
|
||||
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||
@ -738,6 +782,7 @@ func (m *PushNotificationResponse) GetReports() []*PushNotificationReport {
|
||||
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.PushNotification_PushNotificationType", PushNotification_PushNotificationType_name, PushNotification_PushNotificationType_value)
|
||||
proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value)
|
||||
proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration")
|
||||
proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse")
|
||||
@ -754,60 +799,65 @@ func init() {
|
||||
func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) }
|
||||
|
||||
var fileDescriptor_200acd86044eaa5d = []byte{
|
||||
// 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,
|
||||
// 952 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdd, 0x6e, 0xe3, 0x44,
|
||||
0x14, 0xc6, 0x4e, 0xda, 0x24, 0x27, 0x21, 0x75, 0x47, 0x6d, 0xd7, 0x2c, 0x74, 0x09, 0x06, 0x89,
|
||||
0xa8, 0x17, 0x01, 0x15, 0x89, 0x5d, 0x71, 0x45, 0x48, 0x9d, 0xae, 0xd5, 0xc6, 0x0e, 0x13, 0x97,
|
||||
0xd5, 0x4a, 0x48, 0x96, 0x63, 0x4f, 0x5a, 0xab, 0xae, 0xc7, 0x78, 0x26, 0x45, 0xb9, 0xe3, 0x01,
|
||||
0xb8, 0xe1, 0x96, 0xc7, 0xe0, 0x9a, 0x37, 0xe0, 0xa5, 0x90, 0xc7, 0x76, 0x9a, 0x36, 0x6e, 0x5a,
|
||||
0x24, 0xae, 0x92, 0xf3, 0x9d, 0x9f, 0x99, 0x39, 0xdf, 0xf9, 0x8e, 0x41, 0x8d, 0xe7, 0xec, 0xca,
|
||||
0x89, 0x28, 0x0f, 0x66, 0x81, 0xe7, 0xf2, 0x80, 0x46, 0xac, 0x17, 0x27, 0x94, 0x53, 0x54, 0x17,
|
||||
0x3f, 0xd3, 0xf9, 0x4c, 0xfb, 0xbb, 0x0a, 0x9f, 0x8c, 0xe7, 0xec, 0xca, 0x5c, 0x89, 0xc2, 0xe4,
|
||||
0x32, 0x60, 0x3c, 0x11, 0xff, 0x91, 0x05, 0xc0, 0xe9, 0x35, 0x89, 0x1c, 0xbe, 0x88, 0x89, 0x2a,
|
||||
0x75, 0xa4, 0x6e, 0xfb, 0xf8, 0xeb, 0x5e, 0x91, 0xdf, 0xdb, 0x94, 0xdb, 0xb3, 0xd3, 0x44, 0x7b,
|
||||
0x11, 0x13, 0xdc, 0xe0, 0xc5, 0x5f, 0xf4, 0x19, 0xb4, 0x7c, 0x72, 0x1b, 0x78, 0xc4, 0x11, 0x98,
|
||||
0x2a, 0x77, 0xa4, 0x6e, 0x03, 0x37, 0x33, 0x4c, 0x64, 0xa0, 0x2f, 0x61, 0x27, 0x88, 0x18, 0x77,
|
||||
0xc3, 0x50, 0xd4, 0x71, 0x02, 0x5f, 0xad, 0x88, 0xa8, 0xf6, 0x2a, 0x6c, 0xf8, 0x69, 0x2d, 0xd7,
|
||||
0xf3, 0x08, 0x63, 0x79, 0xad, 0x6a, 0x56, 0x2b, 0xc3, 0xb2, 0x5a, 0x2a, 0xd4, 0x48, 0xe4, 0x4e,
|
||||
0x43, 0xe2, 0xab, 0x5b, 0x1d, 0xa9, 0x5b, 0xc7, 0x85, 0x99, 0x7a, 0x6e, 0x49, 0xc2, 0x02, 0x1a,
|
||||
0xa9, 0xdb, 0x1d, 0xa9, 0x5b, 0xc5, 0x85, 0x89, 0xba, 0xa0, 0xb8, 0x61, 0x48, 0x7f, 0x25, 0xbe,
|
||||
0x73, 0x4d, 0x16, 0x4e, 0x18, 0x30, 0xae, 0xd6, 0x3a, 0x95, 0x6e, 0x0b, 0xb7, 0x73, 0xfc, 0x8c,
|
||||
0x2c, 0xce, 0x03, 0xc6, 0xd1, 0x11, 0xec, 0x4e, 0x43, 0xea, 0x5d, 0x13, 0xdf, 0xf1, 0xae, 0x5c,
|
||||
0x9e, 0x85, 0xd6, 0x45, 0xe8, 0x4e, 0xee, 0x18, 0x5c, 0xb9, 0x5c, 0xc4, 0xbe, 0x02, 0x98, 0x47,
|
||||
0x89, 0xe8, 0x0f, 0x49, 0xd4, 0x86, 0xb8, 0xcc, 0x0a, 0x82, 0xf6, 0x60, 0xeb, 0x32, 0x71, 0x23,
|
||||
0xae, 0x42, 0x47, 0xea, 0xb6, 0x70, 0x66, 0xa0, 0xd7, 0xa0, 0x8a, 0x33, 0x9d, 0x59, 0x42, 0x6f,
|
||||
0x1c, 0x8f, 0x46, 0xdc, 0xf5, 0x38, 0x73, 0x68, 0x14, 0x2e, 0xd4, 0xa6, 0xa8, 0xb1, 0x2f, 0xfc,
|
||||
0xc3, 0x84, 0xde, 0x0c, 0x72, 0xaf, 0x15, 0x85, 0x0b, 0xf4, 0x31, 0x34, 0xdc, 0x38, 0x72, 0x38,
|
||||
0x8d, 0x03, 0x4f, 0x6d, 0x89, 0xc6, 0xd4, 0xdd, 0x38, 0xb2, 0x53, 0x5b, 0x1b, 0x42, 0x63, 0x49,
|
||||
0x0e, 0x3a, 0x00, 0x74, 0x61, 0x9e, 0x99, 0xd6, 0x3b, 0xd3, 0xb1, 0xad, 0x33, 0xdd, 0x74, 0xec,
|
||||
0xf7, 0x63, 0x5d, 0xf9, 0x00, 0x7d, 0x08, 0x8d, 0xfe, 0x38, 0xc7, 0x14, 0x09, 0x21, 0x68, 0x0f,
|
||||
0x0d, 0xac, 0xff, 0xd0, 0x9f, 0xe8, 0x39, 0x26, 0x6b, 0x7f, 0xc9, 0xf0, 0xc5, 0xa6, 0x11, 0xc0,
|
||||
0x84, 0xc5, 0x34, 0x62, 0x24, 0x6d, 0x36, 0x9b, 0x0b, 0x5a, 0xc4, 0x0c, 0xd5, 0x71, 0x61, 0x22,
|
||||
0x13, 0xb6, 0x48, 0x92, 0xd0, 0x44, 0x0c, 0x42, 0xfb, 0xf8, 0xcd, 0xf3, 0x66, 0xab, 0x28, 0xdc,
|
||||
0xd3, 0xd3, 0x5c, 0x31, 0x63, 0x59, 0x19, 0x74, 0x08, 0x90, 0x90, 0x5f, 0xe6, 0x84, 0xf1, 0x62,
|
||||
0x6e, 0x5a, 0xb8, 0x91, 0x23, 0x86, 0xaf, 0xfd, 0x26, 0x41, 0x63, 0x99, 0xb3, 0xfa, 0x74, 0x1d,
|
||||
0x63, 0x0b, 0x17, 0x4f, 0xdf, 0x87, 0xdd, 0x51, 0xff, 0x7c, 0x68, 0xe1, 0x91, 0x7e, 0xe2, 0x8c,
|
||||
0xf4, 0xc9, 0xa4, 0x7f, 0xaa, 0x2b, 0x12, 0xda, 0x03, 0xe5, 0x27, 0x1d, 0x4f, 0x0c, 0xcb, 0x74,
|
||||
0x46, 0xc6, 0x64, 0xd4, 0xb7, 0x07, 0x6f, 0x15, 0x19, 0xbd, 0x84, 0x83, 0x0b, 0x73, 0x72, 0x31,
|
||||
0x1e, 0x5b, 0xd8, 0xd6, 0x4f, 0x56, 0x7b, 0x58, 0x49, 0x9b, 0x66, 0x98, 0xb6, 0x8e, 0xcd, 0xfe,
|
||||
0x79, 0x76, 0x82, 0x52, 0xd5, 0xe6, 0xa0, 0xe6, 0x4c, 0x0d, 0xa8, 0x4f, 0xfa, 0xfe, 0x2d, 0x49,
|
||||
0x78, 0xc0, 0xc8, 0x0d, 0x89, 0x38, 0x7a, 0x0f, 0x07, 0x6b, 0xaa, 0x75, 0x82, 0x68, 0x46, 0x55,
|
||||
0xa9, 0x53, 0xe9, 0x36, 0x8f, 0x3f, 0x7f, 0xbc, 0x3d, 0x3f, 0xce, 0x49, 0xb2, 0x30, 0xa2, 0x19,
|
||||
0xc5, 0x7b, 0xf1, 0x03, 0x57, 0x8a, 0x6a, 0x6f, 0x60, 0xbf, 0x34, 0x05, 0x7d, 0x0a, 0xcd, 0x78,
|
||||
0x3e, 0x0d, 0x03, 0x2f, 0x9d, 0x76, 0x26, 0x0e, 0x6a, 0x61, 0xc8, 0xa0, 0x33, 0xb2, 0x60, 0xda,
|
||||
0xef, 0x32, 0x7c, 0xf4, 0xe8, 0x69, 0x6b, 0x22, 0x94, 0xd6, 0x45, 0x58, 0x22, 0x68, 0xb9, 0x54,
|
||||
0xd0, 0x87, 0x00, 0x77, 0x57, 0x29, 0xc8, 0x5b, 0xde, 0xa4, 0x54, 0x98, 0xd5, 0x52, 0x61, 0x2e,
|
||||
0xc5, 0xb4, 0xb5, 0x2a, 0xa6, 0xc7, 0x25, 0x7f, 0x04, 0xbb, 0x8c, 0x24, 0xb7, 0x24, 0x71, 0x56,
|
||||
0xce, 0xaf, 0x89, 0xdc, 0x9d, 0xcc, 0x31, 0x2e, 0x6e, 0xa1, 0xfd, 0x21, 0xc1, 0x61, 0x69, 0x3b,
|
||||
0x96, 0xd3, 0xfe, 0x1a, 0xaa, 0xff, 0x95, 0x33, 0x91, 0x90, 0xbe, 0xff, 0x86, 0x30, 0xe6, 0x5e,
|
||||
0x92, 0xa2, 0x47, 0x2d, 0xdc, 0xc8, 0x11, 0xc3, 0x5f, 0x55, 0x51, 0xe5, 0x9e, 0x8a, 0xb4, 0x7f,
|
||||
0x64, 0x50, 0x1e, 0x16, 0x7f, 0x0e, 0x33, 0x2f, 0xa0, 0x26, 0x16, 0xd7, 0x92, 0x91, 0xed, 0xd4,
|
||||
0x7c, 0x9a, 0x89, 0x12, 0x46, 0xab, 0xa5, 0x8c, 0xaa, 0x50, 0xcb, 0xef, 0x9f, 0x53, 0x51, 0x98,
|
||||
0x68, 0x00, 0x55, 0xf1, 0x4d, 0xd9, 0x16, 0xba, 0xff, 0xea, 0xf1, 0x26, 0xad, 0x01, 0x42, 0xee,
|
||||
0x22, 0x59, 0xb3, 0x61, 0xaf, 0xcc, 0x8b, 0x34, 0x78, 0x55, 0x08, 0x7b, 0x7c, 0x31, 0x79, 0xeb,
|
||||
0x98, 0x96, 0x6d, 0x0c, 0x8d, 0x41, 0xdf, 0x4e, 0xb5, 0x9b, 0x8b, 0xbc, 0x09, 0xb5, 0x3b, 0x69,
|
||||
0x0b, 0xc3, 0x4c, 0xdd, 0x8a, 0xac, 0xc5, 0xf0, 0x62, 0x7d, 0xf9, 0x88, 0x0d, 0x82, 0xbe, 0x85,
|
||||
0x7a, 0xbe, 0x4c, 0x58, 0x4e, 0xef, 0xcb, 0x0d, 0x1b, 0x6b, 0x19, 0xfb, 0x04, 0xb3, 0xda, 0x9f,
|
||||
0x32, 0x1c, 0xac, 0x1f, 0x19, 0xd3, 0x84, 0x6f, 0x58, 0x9d, 0xdf, 0xdf, 0x5f, 0x9d, 0x47, 0x9b,
|
||||
0x56, 0x67, 0x5a, 0xaa, 0x74, 0x59, 0xfe, 0x1f, 0x2c, 0x6b, 0x3f, 0x3f, 0x67, 0xa9, 0xee, 0x40,
|
||||
0xf3, 0x1d, 0xb6, 0xcc, 0xd3, 0xd5, 0x2f, 0xca, 0x83, 0xe5, 0x28, 0xa7, 0x98, 0x69, 0xd9, 0x0e,
|
||||
0xd6, 0x4f, 0x8d, 0x89, 0xad, 0x63, 0xfd, 0x44, 0xa9, 0xa4, 0x0b, 0x73, 0xfd, 0x41, 0xb9, 0xd4,
|
||||
0xee, 0xf7, 0x55, 0x7a, 0xa8, 0x98, 0xef, 0xa0, 0x96, 0x88, 0xb7, 0x33, 0x55, 0x16, 0x6c, 0x75,
|
||||
0x9e, 0x6a, 0x12, 0x2e, 0x12, 0xa6, 0xdb, 0x22, 0xf2, 0x9b, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff,
|
||||
0xc6, 0x95, 0x5d, 0x48, 0x48, 0x09, 0x00, 0x00,
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ message PushNotificationRegistration {
|
||||
bool unregister = 9;
|
||||
bytes grant = 10;
|
||||
bool allow_from_contacts_only = 11;
|
||||
string apn_topic = 12;
|
||||
}
|
||||
|
||||
message PushNotificationRegistrationResponse {
|
||||
@ -65,6 +66,12 @@ message PushNotification {
|
||||
bytes public_key = 3;
|
||||
string installation_id = 4;
|
||||
bytes message = 5;
|
||||
PushNotificationType type = 6;
|
||||
enum PushNotificationType {
|
||||
UNKNOWN_PUSH_NOTIFICATION_TYPE = 0;
|
||||
MESSAGE = 1;
|
||||
MENTION = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message PushNotificationRequest {
|
||||
|
@ -17,6 +17,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/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
@ -26,6 +27,7 @@ import (
|
||||
const (
|
||||
bob1DeviceToken = "token-1"
|
||||
bob2DeviceToken = "token-2"
|
||||
testAPNTopic = "topic"
|
||||
)
|
||||
|
||||
func TestMessengerPushNotificationSuite(t *testing.T) {
|
||||
@ -54,6 +56,7 @@ func (s *MessengerPushNotificationSuite) SetupTest() {
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
s.Require().NoError(s.m.Start())
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TearDownTest() {
|
||||
@ -124,6 +127,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
||||
|
||||
bob1 := s.m
|
||||
bob2 := s.newMessengerWithKey(s.shh, s.m.identity)
|
||||
s.Require().NoError(bob2.Start())
|
||||
|
||||
serverKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
@ -139,7 +143,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
||||
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
@ -169,7 +173,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
||||
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
@ -319,7 +323,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO
|
||||
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Pull servers and check we registered
|
||||
@ -430,6 +434,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
|
||||
alice := s.newMessenger(s.shh)
|
||||
// another contact to invalidate the token
|
||||
frank := s.newMessenger(s.shh)
|
||||
s.Require().NoError(frank.Start())
|
||||
// start alice and enable push notifications
|
||||
s.Require().NoError(alice.Start())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
@ -463,7 +468,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
|
||||
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Pull servers and check we registered
|
||||
@ -650,7 +655,7 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
|
||||
err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
@ -680,7 +685,7 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
|
||||
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
@ -796,3 +801,54 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
|
||||
s.Require().NoError(bob2.Shutdown())
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TestContactCode() {
|
||||
|
||||
bob1 := s.m
|
||||
|
||||
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())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
|
||||
// Register bob1
|
||||
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
|
||||
|
||||
// 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)
|
||||
|
||||
contactCodeAdvertisement, err := bob1.buildContactCodeAdvertisement()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(contactCodeAdvertisement)
|
||||
|
||||
s.Require().NoError(alice.pushNotificationClient.HandleContactCodeAdvertisement(&bob1.identity.PublicKey, *contactCodeAdvertisement))
|
||||
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
s.Require().NoError(server.Shutdown())
|
||||
}
|
||||
|
@ -121,9 +121,6 @@ type Config struct {
|
||||
InstallationID string
|
||||
|
||||
Logger *zap.Logger
|
||||
|
||||
// TokenType is the type of token
|
||||
TokenType protobuf.PushNotificationRegistration_TokenType
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
@ -141,6 +138,10 @@ type Client struct {
|
||||
AccessToken string
|
||||
// deviceToken is the device token for this device
|
||||
deviceToken string
|
||||
// TokenType is the type of token
|
||||
tokenType protobuf.PushNotificationRegistration_TokenType
|
||||
// APNTopic is the topic of the apn topic for push notification
|
||||
apnTopic string
|
||||
|
||||
// randomReader only used for testing so we have deterministic encryption
|
||||
reader io.Reader
|
||||
@ -155,6 +156,9 @@ type Client struct {
|
||||
resendingLoopQuitChan chan struct{}
|
||||
|
||||
quit chan struct{}
|
||||
|
||||
// registrationSubscriptions is a list of chan of client subscribed to the registration event
|
||||
registrationSubscriptions []chan struct{}
|
||||
}
|
||||
|
||||
func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client {
|
||||
@ -179,16 +183,38 @@ func (c *Client) Start() error {
|
||||
|
||||
c.subscribeForSentMessages()
|
||||
c.subscribeForScheduledMessages()
|
||||
|
||||
// We start even if push notifications are disabled, as we might
|
||||
// actually be sending an unregister message
|
||||
c.startRegistrationLoop()
|
||||
|
||||
c.startResendingLoop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) publishOnRegistrationSubscriptions() {
|
||||
// Publish on channels, drop if buffer is full
|
||||
for _, s := range c.registrationSubscriptions {
|
||||
select {
|
||||
case s <- struct{}{}:
|
||||
default:
|
||||
c.config.Logger.Warn("subscription channel full, dropping message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) quitRegistrationSubscriptions() {
|
||||
for _, s := range c.registrationSubscriptions {
|
||||
close(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Stop() error {
|
||||
close(c.quit)
|
||||
c.stopRegistrationLoop()
|
||||
c.stopResendingLoop()
|
||||
c.quitRegistrationSubscriptions()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -197,6 +223,8 @@ func (c *Client) Unregister() error {
|
||||
// stop registration loop
|
||||
c.stopRegistrationLoop()
|
||||
|
||||
c.config.RemoteNotificationsEnabled = false
|
||||
|
||||
registration := c.buildPushNotificationUnregisterMessage()
|
||||
err := c.saveLastPushNotificationRegistration(registration, nil)
|
||||
if err != nil {
|
||||
@ -230,6 +258,12 @@ func (c *Client) Registered() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *Client) SubscribeToRegistrations() chan struct{} {
|
||||
s := make(chan struct{}, 100)
|
||||
c.registrationSubscriptions = append(c.registrationSubscriptions, s)
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *Client) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) {
|
||||
return c.persistence.GetSentNotification(hashedPublicKey, installationID, messageID)
|
||||
}
|
||||
@ -245,14 +279,21 @@ func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
|
||||
if !c.config.RemoteNotificationsEnabled {
|
||||
c.config.Logger.Info("remote notifications not enabled, not registering")
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
|
||||
}
|
||||
|
||||
// Register registers with all the servers
|
||||
func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
|
||||
func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
|
||||
// stop registration loop
|
||||
c.stopRegistrationLoop()
|
||||
|
||||
c.config.RemoteNotificationsEnabled = true
|
||||
|
||||
// reset servers
|
||||
err := c.resetServers()
|
||||
if err != nil {
|
||||
@ -260,6 +301,8 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut
|
||||
}
|
||||
|
||||
c.deviceToken = deviceToken
|
||||
c.apnTopic = apnTopic
|
||||
c.tokenType = tokenType
|
||||
|
||||
registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs)
|
||||
if err != nil {
|
||||
@ -299,7 +342,60 @@ func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.Pub
|
||||
server.Registered = true
|
||||
server.RegisteredAt = time.Now().Unix()
|
||||
|
||||
return c.persistence.UpsertServer(server)
|
||||
err = c.persistence.UpsertServer(server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.publishOnRegistrationSubscriptions()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processQueryInfo takes info about push notifications and validates them
|
||||
func (c *Client) processQueryInfo(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, info *protobuf.PushNotificationQueryInfo) error {
|
||||
// make sure the public key matches
|
||||
if !bytes.Equal(info.PublicKey, common.HashPublicKey(clientPublicKey)) {
|
||||
c.config.Logger.Warn("reply for different key, ignoring")
|
||||
return errors.New("reply for a different key, ignoring")
|
||||
}
|
||||
|
||||
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(clientPublicKey, info.AllowedKeyList)
|
||||
|
||||
}
|
||||
|
||||
// no luck
|
||||
if len(accessToken) == 0 {
|
||||
c.config.Logger.Debug("not in the allowed key list")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(clientPublicKey, serverPublicKey, info.Grant, accessToken); err != nil {
|
||||
c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
pushNotificationInfo := &PushNotificationInfo{
|
||||
PublicKey: clientPublicKey,
|
||||
ServerPublicKey: serverPublicKey,
|
||||
AccessToken: accessToken,
|
||||
InstallationID: info.InstallationId,
|
||||
Version: info.Version,
|
||||
RetrievedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
err := c.persistence.SavePushNotificationInfo([]*PushNotificationInfo{pushNotificationInfo})
|
||||
if err != nil {
|
||||
c.config.Logger.Error("failed to save push notifications", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandlePushNotificationQueryResponse should update the data in the database for a given user
|
||||
@ -310,63 +406,55 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ
|
||||
}
|
||||
|
||||
// get the public key associated with this query
|
||||
publicKey, err := c.persistence.GetQueryPublicKey(response.MessageId)
|
||||
clientPublicKey, err := c.persistence.GetQueryPublicKey(response.MessageId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if publicKey == nil {
|
||||
if clientPublicKey == nil {
|
||||
c.config.Logger.Debug("query not found")
|
||||
return nil
|
||||
}
|
||||
|
||||
var pushNotificationInfo []*PushNotificationInfo
|
||||
// process query, make sure to validate grant as coming from the server
|
||||
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")
|
||||
err := c.processQueryInfo(clientPublicKey, serverPublicKey, info)
|
||||
if err != nil {
|
||||
|
||||
c.config.Logger.Warn("failed to process info", zap.Any("info", info), zap.Error(err))
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
// HandleContactCodeAdvertisement checks if there are any info and process them
|
||||
func (c *Client) HandleContactCodeAdvertisement(clientPublicKey *ecdsa.PublicKey, message protobuf.ContactCodeAdvertisement) error {
|
||||
// nothing to do for our own pubkey
|
||||
if common.IsPubKeyEqual(clientPublicKey, &c.config.Identity.PublicKey) {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.config.Logger.Debug("received contact code advertisement", zap.Any("advertisement", message))
|
||||
for _, info := range message.PushNotificationInfo {
|
||||
c.config.Logger.Debug("handling push notification query info")
|
||||
serverPublicKey, err := crypto.DecompressPubkey(info.ServerPublicKey)
|
||||
if err != nil {
|
||||
c.config.Logger.Error("could not unmarshal server pubkey", zap.Binary("server-key", info.ServerPublicKey))
|
||||
return err
|
||||
}
|
||||
err = c.processQueryInfo(clientPublicKey, serverPublicKey, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Save query so that we won't query again to early
|
||||
// NOTE: this is not very accurate as we might fetch an historical message,
|
||||
// prolonging the time that we fetch new info.
|
||||
// Most of the times it should work fine, as if the info are stale they'd be
|
||||
// fetched again because of an error response from the push notification server
|
||||
return c.persistence.SavePushNotificationQuery(clientPublicKey, []byte(uuid.New().String()))
|
||||
}
|
||||
|
||||
// HandlePushNotificationResponse should set the request as processed
|
||||
@ -427,6 +515,10 @@ func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installatio
|
||||
return c.persistence.GetPushNotificationInfo(publicKey, installationIDs)
|
||||
}
|
||||
|
||||
func (c *Client) Enabled() bool {
|
||||
return c.config.RemoteNotificationsEnabled
|
||||
}
|
||||
|
||||
func (c *Client) EnableSending() {
|
||||
c.config.SendEnabled = true
|
||||
}
|
||||
@ -436,17 +528,21 @@ func (c *Client) DisableSending() {
|
||||
}
|
||||
|
||||
func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
|
||||
c.config.Logger.Debug("enabling push notification from contacts only")
|
||||
c.config.AllowFromContactsOnly = true
|
||||
if c.lastPushNotificationRegistration != nil {
|
||||
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
|
||||
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
|
||||
c.config.Logger.Debug("re-registering after enabling push notifications from contacts only")
|
||||
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
|
||||
c.config.Logger.Debug("disabling push notification from contacts only")
|
||||
c.config.AllowFromContactsOnly = false
|
||||
if c.lastPushNotificationRegistration != nil {
|
||||
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
|
||||
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
|
||||
c.config.Logger.Debug("re-registering after disabling push notifications from contacts only")
|
||||
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -545,8 +641,9 @@ func (c *Client) loadLastPushNotificationRegistration() error {
|
||||
c.lastContactIDs = lastContactIDs
|
||||
c.lastPushNotificationRegistration = lastRegistration
|
||||
c.deviceToken = lastRegistration.DeviceToken
|
||||
c.apnTopic = lastRegistration.ApnTopic
|
||||
c.tokenType = lastRegistration.TokenType
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (c *Client) stopRegistrationLoop() {
|
||||
@ -807,7 +904,7 @@ func (c *Client) allowedKeyList(token []byte, contactIDs []*ecdsa.PublicKey) ([]
|
||||
// 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) {
|
||||
if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs, c.lastPushNotificationRegistration.AllowFromContactsOnly, c.config.AllowFromContactsOnly) {
|
||||
c.config.Logger.Info("refreshing access token")
|
||||
return uuid.New().String()
|
||||
}
|
||||
@ -830,7 +927,8 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu
|
||||
|
||||
options := &protobuf.PushNotificationRegistration{
|
||||
AccessToken: token,
|
||||
TokenType: c.config.TokenType,
|
||||
TokenType: c.tokenType,
|
||||
ApnTopic: c.apnTopic,
|
||||
Version: c.getVersion(),
|
||||
InstallationId: c.config.InstallationID,
|
||||
DeviceToken: c.deviceToken,
|
||||
@ -851,8 +949,17 @@ func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotifica
|
||||
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 {
|
||||
// shouldRefreshToken tells us whether we should create a new token,
|
||||
// that's only necessary when a contact is removed
|
||||
// or allowFromContactsOnly is enabled.
|
||||
// In both cases we want to invalidate any existing token
|
||||
func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.PublicKey, oldAllowFromContactsOnly, newAllowFromContactsOnly bool) bool {
|
||||
|
||||
// Check if allowFromContactsOnly has just been enabled
|
||||
if !oldAllowFromContactsOnly && newAllowFromContactsOnly {
|
||||
return true
|
||||
}
|
||||
|
||||
newContactIDsMap := make(map[string]bool)
|
||||
for _, pk := range newContactIDs {
|
||||
newContactIDsMap[types.EncodeHex(crypto.FromECDSAPub(pk))] = true
|
||||
@ -1017,6 +1124,7 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []
|
||||
for _, i := range infos {
|
||||
// TODO: Add ChatID, message, public_key
|
||||
pushNotifications = append(pushNotifications, &protobuf.PushNotification{
|
||||
Type: protobuf.PushNotification_MESSAGE,
|
||||
AccessToken: i.AccessToken,
|
||||
PublicKey: common.HashPublicKey(publicKey),
|
||||
InstallationId: i.InstallationID,
|
||||
@ -1193,7 +1301,6 @@ func (c *Client) saveLastPushNotificationRegistration(registration *protobuf.Pus
|
||||
c.lastPushNotificationRegistration = registration
|
||||
c.lastContactIDs = contactIDs
|
||||
|
||||
c.startRegistrationLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1245,6 +1352,48 @@ func (c *Client) handleAllowedKeyList(publicKey *ecdsa.PublicKey, allowedKeyList
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *Client) MyPushNotificationQueryInfo() ([]*protobuf.PushNotificationQueryInfo, error) {
|
||||
|
||||
// Nothing to do
|
||||
if c.lastPushNotificationRegistration == nil || c.lastPushNotificationRegistration.Unregister {
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
var response []*protobuf.PushNotificationQueryInfo
|
||||
servers, err := c.persistence.GetServers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, server := range servers {
|
||||
// ignore non-registered servers
|
||||
if !server.Registered {
|
||||
continue
|
||||
}
|
||||
// build grant for this specific server
|
||||
grant, err := c.buildGrantSignature(server.PublicKey, c.lastPushNotificationRegistration.AccessToken)
|
||||
if err != nil {
|
||||
c.config.Logger.Error("failed to build grant", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queryInfo := &protobuf.PushNotificationQueryInfo{
|
||||
InstallationId: c.config.InstallationID,
|
||||
// is this the right key?
|
||||
PublicKey: common.HashPublicKey(&c.config.Identity.PublicKey),
|
||||
Version: c.lastPushNotificationRegistration.Version,
|
||||
Grant: grant,
|
||||
ServerPublicKey: crypto.CompressPubkey(server.PublicKey),
|
||||
}
|
||||
if c.lastPushNotificationRegistration.AllowFromContactsOnly {
|
||||
queryInfo.AllowedKeyList = c.lastPushNotificationRegistration.AllowedKeyList
|
||||
} else {
|
||||
queryInfo.AccessToken = c.lastPushNotificationRegistration.AccessToken
|
||||
}
|
||||
response = append(response, queryInfo)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -219,12 +219,17 @@ func (s *ClientSuite) TestShouldRefreshToken() {
|
||||
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}))
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}, true, true))
|
||||
|
||||
// everything the same
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}))
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, true))
|
||||
|
||||
// A contact is removed
|
||||
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}))
|
||||
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}, true, true))
|
||||
|
||||
// allow from contacts only is disabled
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, false))
|
||||
|
||||
// allow from contacts only is enabled
|
||||
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, false, true))
|
||||
}
|
||||
|
@ -4,8 +4,11 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
@ -21,6 +24,7 @@ type GoRushRequestNotification struct {
|
||||
Tokens []string `json:"tokens"`
|
||||
Platform uint `json:"platform"`
|
||||
Message string `json:"message"`
|
||||
Topic string `json:"topic"`
|
||||
Data *GoRushRequestData `json:"data"`
|
||||
}
|
||||
|
||||
@ -53,6 +57,7 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
|
||||
Tokens: []string{registration.DeviceToken},
|
||||
Platform: tokenTypeToGoRushPlatform(registration.TokenType),
|
||||
Message: defaultNotificationMessage,
|
||||
Topic: registration.ApnTopic,
|
||||
Data: &GoRushRequestData{
|
||||
EncryptedMessage: hex.EncodeToString(request.Message),
|
||||
ChatID: request.ChatId,
|
||||
@ -63,15 +68,20 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
|
||||
return goRushRequests
|
||||
}
|
||||
|
||||
func sendGoRushNotification(request *GoRushRequest, url string) error {
|
||||
func sendGoRushNotification(request *GoRushRequest, url string, logger *zap.Logger) error {
|
||||
payload, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
|
||||
response, err := http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
logger.Info("Sent gorush request", zap.String("response", string(body)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -16,9 +16,14 @@ 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 return all the public keys stored
|
||||
GetPushNotificationRegistrationPublicKeys() ([][]byte, error)
|
||||
|
||||
//GetPushNotificationRegistrationVersion returns the latest version or 0 for a given pk and installationID
|
||||
GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error)
|
||||
// UnregisterPushNotificationRegistration unregister a given pk/installationID
|
||||
UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error
|
||||
|
||||
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
|
||||
DeletePushNotificationRegistration(publicKey []byte, installationID string) error
|
||||
// SavePushNotificationRegistration saves a push notification option to the db
|
||||
@ -39,7 +44,7 @@ func NewSQLitePersistence(db *sql.DB) Persistence {
|
||||
|
||||
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 = ?`, publicKey, installationID).Scan(&marshaledRegistration)
|
||||
err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? AND registration IS NOT NULL`, publicKey, installationID).Scan(&marshaledRegistration)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
@ -48,13 +53,25 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstall
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
|
||||
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registration, nil
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error) {
|
||||
var version uint64
|
||||
err := p.db.QueryRow(`SELECT version FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&version)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
type PushNotificationIDAndRegistration struct {
|
||||
ID []byte
|
||||
Registration *protobuf.PushNotificationRegistration
|
||||
@ -70,7 +87,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...) // nolint: gosec
|
||||
rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE registration IS NOT NULL AND public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -86,6 +103,10 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
// Skip if there's no registration
|
||||
if marshaledRegistration == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
|
||||
return nil, err
|
||||
@ -97,7 +118,7 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) {
|
||||
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations`)
|
||||
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations WHERE registration IS NOT NULL`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -126,6 +147,11 @@ func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, r
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error {
|
||||
_, err := p.db.Exec(`UPDATE push_notification_server_registrations SET registration = NULL, version = ? WHERE public_key = ? AND installation_id = ?`, version, publicKey, installationID)
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -233,12 +233,12 @@ func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte
|
||||
return nil, ErrMalformedPushNotificationRegistrationInstallationID
|
||||
}
|
||||
|
||||
previousRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId)
|
||||
previousVersion, err := s.persistence.GetPushNotificationRegistrationVersion(common.HashPublicKey(publicKey), registration.InstallationId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if previousRegistration != nil && registration.Version <= previousRegistration.Version {
|
||||
if registration.Version <= previousVersion {
|
||||
return nil, ErrInvalidPushNotificationRegistrationVersion
|
||||
}
|
||||
|
||||
@ -290,6 +290,7 @@ func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotifica
|
||||
for _, idAndResponse := range registrations {
|
||||
|
||||
registration := idAndResponse.Registration
|
||||
|
||||
info := &protobuf.PushNotificationQueryInfo{
|
||||
PublicKey: idAndResponse.ID,
|
||||
Grant: registration.Grant,
|
||||
@ -333,6 +334,11 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
|
||||
InstallationId: pn.InstallationId,
|
||||
}
|
||||
|
||||
if pn.Type != protobuf.PushNotification_MESSAGE {
|
||||
s.config.Logger.Warn("unhandled type")
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
|
||||
report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||
@ -340,7 +346,6 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
|
||||
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
|
||||
@ -362,7 +367,7 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
|
||||
|
||||
// This can be done asynchronously
|
||||
goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
|
||||
err := sendGoRushNotification(goRushRequest, s.config.GorushURL)
|
||||
err := sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger)
|
||||
if err != nil {
|
||||
s.config.Logger.Error("failed to send go rush notification", zap.Error(err))
|
||||
// TODO: handle this error?
|
||||
@ -403,12 +408,9 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ
|
||||
}
|
||||
|
||||
if registration.Unregister {
|
||||
s.config.Logger.Info("unregistering client")
|
||||
// We save an empty registration, only keeping version and installation-id
|
||||
emptyRegistration := &protobuf.PushNotificationRegistration{
|
||||
Version: registration.Version,
|
||||
InstallationId: registration.InstallationId,
|
||||
}
|
||||
if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil {
|
||||
if err := s.persistence.UnregisterPushNotificationRegistration(common.HashPublicKey(publicKey), registration.InstallationId, registration.Version); err != nil {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||
s.config.Logger.Error("failed to unregister ", zap.Error(err))
|
||||
return response
|
||||
|
@ -500,16 +500,16 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() {
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().True(response.Success)
|
||||
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
|
||||
|
||||
// Check is gone from the db
|
||||
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.DeviceToken)
|
||||
s.Require().Equal(uint64(2), retrievedRegistration.Version)
|
||||
s.Require().Equal(s.installationID, retrievedRegistration.InstallationId)
|
||||
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
|
||||
s.Require().Nil(retrievedRegistration)
|
||||
// Check version is maintained
|
||||
version, err := s.persistence.GetPushNotificationRegistrationVersion(common.HashPublicKey(&s.key.PublicKey), s.installationID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(uint64(2), version)
|
||||
}
|
||||
|
||||
func (s *ServerSuite) TestbuildPushNotificationQueryResponseNoFiltering() {
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/datasync"
|
||||
"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"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
@ -45,6 +47,11 @@ type StatusMessage struct {
|
||||
TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
|
||||
// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
|
||||
ApplicationMetadataLayerSigPubKey *ecdsa.PublicKey `json:"-"`
|
||||
|
||||
// Installations is the new installations returned by the encryption layer
|
||||
Installations []*multidevice.Installation
|
||||
// SharedSecret is the shared secret returned by the encryption layer
|
||||
SharedSecrets []*sharedsecret.Secret
|
||||
}
|
||||
|
||||
// Temporary JSON marshaling for those messages that are not yet processed
|
||||
@ -117,7 +124,7 @@ func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecd
|
||||
return errors.Wrap(err, "failed to unmarshal ProtocolMessage")
|
||||
}
|
||||
|
||||
payload, err := enc.HandleMessage(
|
||||
response, err := enc.HandleMessage(
|
||||
myKey,
|
||||
senderKey,
|
||||
&protocolMessage,
|
||||
@ -128,7 +135,9 @@ func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecd
|
||||
return errors.Wrap(err, "failed to handle Encryption message")
|
||||
}
|
||||
|
||||
m.DecryptedPayload = payload
|
||||
m.DecryptedPayload = response.DecryptedMessage
|
||||
m.Installations = response.Installations
|
||||
m.SharedSecrets = response.SharedSecrets
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -217,7 +226,6 @@ func (m *StatusMessage) HandleApplication() error {
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION:
|
||||
return m.unmarshalProtobufData(new(protobuf.PairInstallation))
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
|
||||
return m.unmarshalProtobufData(new(protobuf.ContactCodeAdvertisement))
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:
|
||||
|
@ -430,7 +430,7 @@ func (api *PublicAPI) StopPushNotificationsServer() error {
|
||||
|
||||
// PushNotification client endpoints
|
||||
|
||||
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
|
||||
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
|
||||
// We set both for now as they are equivalent
|
||||
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", true)
|
||||
if err != nil {
|
||||
@ -441,10 +441,10 @@ func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceTo
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken)
|
||||
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken, apnTopic, tokenType)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error {
|
||||
func (api *PublicAPI) UnregisterFromPushNotifications(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -169,10 +169,14 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
|
||||
func (s *Service) StartMessenger() error {
|
||||
// Start a loop that retrieves all messages and propagates them to status-react.
|
||||
s.cancelMessenger = make(chan struct{})
|
||||
err := s.messenger.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.retrieveMessagesLoop(time.Second, s.cancelMessenger)
|
||||
go s.verifyTransactionLoop(30*time.Second, s.cancelMessenger)
|
||||
go s.verifyENSLoop(30*time.Second, s.cancelMessenger)
|
||||
return s.messenger.Start()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) retrieveMessagesLoop(tick time.Duration, cancel <-chan struct{}) {
|
||||
@ -456,6 +460,7 @@ func buildMessengerOptions(
|
||||
) ([]protocol.Option, error) {
|
||||
options := []protocol.Option{
|
||||
protocol.WithCustomLogger(logger),
|
||||
protocol.WithPushNotifications(),
|
||||
protocol.WithDatabase(db),
|
||||
protocol.WithEnvelopesMonitorConfig(envelopesMonitorConfig),
|
||||
protocol.WithOnNegotiatedFilters(onNegotiatedFilters),
|
||||
|
Loading…
x
Reference in New Issue
Block a user