Dont sent messages to user who have not joined

This commit is contained in:
Andrea Maria Piana 2020-09-03 11:54:05 +02:00
parent 2d525f9503
commit 28e06daf6d
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
4 changed files with 35 additions and 169 deletions

View File

@ -115,6 +115,16 @@ func (c *Chat) MembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
return stringSliceToPublicKeys(publicKeys, true) return stringSliceToPublicKeys(publicKeys, true)
} }
func (c *Chat) JoinedMembersAsPublicKeys() ([]*ecdsa.PublicKey, error) {
var publicKeys []string
for _, item := range c.Members {
if item.Joined {
publicKeys = append(publicKeys, item.ID)
}
}
return stringSliceToPublicKeys(publicKeys, true)
}
func (c *Chat) HasMember(memberID string) bool { func (c *Chat) HasMember(memberID string) bool {
for _, member := range c.Members { for _, member := range c.Members {
if memberID == member.ID { if memberID == member.ID {

View File

@ -1663,9 +1663,18 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
case ChatTypePrivateGroupChat: case ChatTypePrivateGroupChat:
logger.Debug("sending group message", zap.String("chatName", chat.Name)) logger.Debug("sending group message", zap.String("chatName", chat.Name))
if spec.Recipients == nil { if spec.Recipients == nil {
spec.Recipients, err = chat.MembersAsPublicKeys() // Chat messages are only dispatched to users who joined
if err != nil { if spec.MessageType == protobuf.ApplicationMetadataMessage_CHAT_MESSAGE {
return nil, err spec.Recipients, err = chat.JoinedMembersAsPublicKeys()
if err != nil {
return nil, err
}
} else {
spec.Recipients, err = chat.MembersAsPublicKeys()
if err != nil {
return nil, err
}
} }
} }
hasPairedDevices := m.hasPairedDevices() hasPairedDevices := m.hasPairedDevices()
@ -3519,7 +3528,7 @@ func (m *Messenger) pushNotificationOptions() *pushnotificationclient.Registrati
var publicChatIDs []string var publicChatIDs []string
for _, contact := range m.allContacts { for _, contact := range m.allContacts {
if contact.IsAdded() { if contact.IsAdded() && !contact.IsBlocked() {
pk, err := contact.PublicKey() pk, err := contact.PublicKey()
if err != nil { if err != nil {
m.logger.Warn("could not parse contact public key") m.logger.Warn("could not parse contact public key")

View File

@ -641,168 +641,6 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
s.Require().NoError(server.Shutdown()) s.Require().NoError(server.Shutdown())
} }
// Here bob acts as his own server
func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer() {
bob1 := s.m
server := s.newPushNotificationServer(s.shh, s.m.identity)
bob2 := server
alice := s.newMessenger(s.shh)
// start alice and enable sending push notifications
s.Require().NoError(alice.Start())
s.Require().NoError(alice.EnableSendingPushNotifications())
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
// Register bob1
err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
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)
bob1Servers, err := bob1.GetPushNotificationsServers()
s.Require().NoError(err)
// Register bob2
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey, pushnotificationclient.ServerTypeCustom)
s.Require().NoError(err)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob2.RetrieveAll()
if err != nil {
return err
}
registered, err := bob2.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
bob2Servers, err := bob2.GetPushNotificationsServers()
s.Require().NoError(err)
// Create one to one chat & send message
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
s.Require().NoError(alice.SaveChat(&chat))
inputMessage := buildTestMessage(chat)
response, err := alice.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
messageIDString := response.Messages[0].ID
messageID, err := hex.DecodeString(messageIDString[2:])
s.Require().NoError(err)
var info []*pushnotificationclient.PushNotificationInfo
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
if err != nil {
return err
}
// Check we have replies for both bob1 and bob2
if len(info) != 2 {
return errors.New("info not fetched")
}
return nil
})
s.Require().NoError(err)
// Check we have replies for both bob1 and bob2
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
if info[0].AccessToken == bob1Servers[0].AccessToken {
bob1Info = info[0]
bob2Info = info[1]
} else {
bob2Info = info[0]
bob1Info = info[1]
}
s.Require().NotNil(bob1Info)
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
s.Require().NotNil(bob2Info)
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
s.Require().NoError(err)
s.Require().NotNil(retrievedNotificationInfo)
s.Require().Len(retrievedNotificationInfo, 2)
var sentNotification *pushnotificationclient.SentNotification
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = alice.RetrieveAll()
if err != nil {
return err
}
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
if err != nil {
return err
}
if sentNotification == nil {
return errors.New("sent notification not found")
}
if !sentNotification.Success {
return errors.New("sent notification not successul")
}
return nil
})
s.Require().NoError(err)
s.Require().NoError(bob2.Shutdown())
s.Require().NoError(alice.Shutdown())
}
func (s *MessengerPushNotificationSuite) TestContactCode() { func (s *MessengerPushNotificationSuite) TestContactCode() {
bob1 := s.m bob1 := s.m

View File

@ -363,12 +363,14 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti
// collect successful requests & registrations // collect successful requests & registrations
var requestAndRegistrations []*RequestAndRegistration var requestAndRegistrations []*RequestAndRegistration
// TODO: this logic needs to be extracted and tested
for _, pn := range request.Requests { for _, pn := range request.Requests {
registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId) registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId)
report := &protobuf.PushNotificationReport{ report := &protobuf.PushNotificationReport{
PublicKey: pn.PublicKey, PublicKey: pn.PublicKey,
InstallationId: pn.InstallationId, InstallationId: pn.InstallationId,
} }
s.config.Logger.Info("registration and request", zap.Any("registration", registration), zap.Any("request", pn))
if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE { if pn.Type == protobuf.PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE {
s.config.Logger.Warn("unhandled type") s.config.Logger.Warn("unhandled type")
@ -382,14 +384,17 @@ func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNoti
s.config.Logger.Warn("empty registration") s.config.Logger.Warn("empty registration")
report.Error = protobuf.PushNotificationReport_NOT_REGISTERED report.Error = protobuf.PushNotificationReport_NOT_REGISTERED
} else if registration.AccessToken != pn.AccessToken { } else if registration.AccessToken != pn.AccessToken {
s.config.Logger.Debug("invalid token")
report.Error = protobuf.PushNotificationReport_WRONG_TOKEN report.Error = protobuf.PushNotificationReport_WRONG_TOKEN
} else if s.contains(registration.BlockedChatList, pn.ChatId) || !s.isValidMentionNotification(pn, registration) { } else if s.contains(registration.BlockedChatList, pn.ChatId) || (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration)) {
// We report as successful but don't send the notification // We report as successful but don't send the notification
// for privacy reasons, as otherwise we would disclose that // for privacy reasons, as otherwise we would disclose that
// the sending client has been blocked or that the registering // the sending client has been blocked or that the registering
// client has not joined a given public chat // client has not joined a given public chat
report.Success = true report.Success = true
s.config.Logger.Debug("invalid token", zap.Bool("blocked", s.contains(registration.BlockedChatList, pn.ChatId)), zap.Bool("mention", (s.isMentionNotification(pn) && !s.isValidMentionNotification(pn, registration))))
} else { } else {
s.config.Logger.Debug("sending push notification")
// For now we just assume that the notification will be successful // For now we just assume that the notification will be successful
requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{
Request: pn, Request: pn,
@ -474,6 +479,10 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ
return response return response
} }
func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { func (s *Server) isMentionNotification(pn *protobuf.PushNotification) bool {
return !registration.BlockMentions && pn.Type == protobuf.PushNotification_MENTION && s.contains(registration.AllowedMentionsChatList, pn.ChatId) return pn.Type == protobuf.PushNotification_MENTION
}
func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool {
return s.isMentionNotification(pn) && !registration.BlockMentions && s.contains(registration.AllowedMentionsChatList, pn.ChatId)
} }