From 18877cae6fb52dc74275ecb81e56f85fc2ef491e Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 3 Sep 2020 08:53:26 +0200 Subject: [PATCH] Handle mentions on server --- protocol/pushnotificationserver/server.go | 54 +++++++----- .../pushnotificationserver/server_test.go | 88 +++++++++++++++++++ 2 files changed, 120 insertions(+), 22 deletions(-) diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index 73a1b4f7b..bac78ac8d 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -152,10 +152,16 @@ func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, return nil } - response := s.buildPushNotificationRequestResponseAndSendNotification(&request) + response, requestsAndRegistrations := s.buildPushNotificationRequestResponse(&request) + //AndSendNotification(&request) if response == nil { return nil } + err = s.sendPushNotification(requestsAndRegistrations) + if err != nil { + s.config.Logger.Error("failed to send go rush notification", zap.Error(err)) + return err + } encodedMessage, err := proto.Marshal(response) if err != nil { return err @@ -334,23 +340,22 @@ func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotifica return response } -func (s *Server) blockedChatID(blockedChatIDs [][]byte, chatID []byte) bool { - for _, blockedChatID := range blockedChatIDs { - if bytes.Equal(blockedChatID, chatID) { +func (s *Server) contains(list [][]byte, chatID []byte) bool { + for _, list := range list { + if bytes.Equal(list, chatID) { return true } } return false } -// buildPushNotificationRequestResponseAndSendNotification will build a response -// and fire-and-forget send a query to the gorush instance -func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse { +// buildPushNotificationRequestResponse will build a response +func (s *Server) buildPushNotificationRequestResponse(request *protobuf.PushNotificationRequest) (*protobuf.PushNotificationResponse, []*RequestAndRegistration) { response := &protobuf.PushNotificationResponse{} // We don't even send a response in this case if request == nil || len(request.MessageId) == 0 { s.config.Logger.Warn("empty message id") - return nil + return nil, nil } response.MessageId = request.MessageId @@ -378,10 +383,10 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request report.Error = protobuf.PushNotificationReport_NOT_REGISTERED } else if registration.AccessToken != pn.AccessToken { report.Error = protobuf.PushNotificationReport_WRONG_TOKEN - } else if s.blockedChatID(registration.BlockedChatList, pn.ChatId) { + } else if s.contains(registration.BlockedChatList, pn.ChatId) { // We report as successful but don't send the notification report.Success = true - } else { + } else if s.isMessageNotification(pn) || s.isValidMentionNotification(pn, registration) { // For now we just assume that the notification will be successful requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{ Request: pn, @@ -389,27 +394,24 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request }) report.Success = true } - response.Reports = append(response.Reports, report) } s.config.Logger.Info("built pn request") if len(requestAndRegistrations) == 0 { s.config.Logger.Warn("no request and registration") - return response + return response, nil } - // This can be done asynchronously + return response, requestAndRegistrations +} + +func (s *Server) sendPushNotification(requestAndRegistrations []*RequestAndRegistration) error { + if len(requestAndRegistrations) == 0 { + return nil + } goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations) - 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? - // GoRush will not let us know that the sending of the push notification has failed, - // so this likely mean that the actual HTTP request has failed, or there was some unexpected error - } - - return response + return sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger) } // listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key @@ -468,3 +470,11 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ return response } + +func (s *Server) isMessageNotification(pn *protobuf.PushNotification) bool { + return pn.Type == protobuf.PushNotification_MESSAGE +} + +func (s *Server) isValidMentionNotification(pn *protobuf.PushNotification, registration *protobuf.PushNotificationRegistration) bool { + return !registration.BlockMentions && pn.Type == protobuf.PushNotification_MENTION && s.contains(registration.AllowedMentionsChatList, pn.ChatId) +} diff --git a/protocol/pushnotificationserver/server_test.go b/protocol/pushnotificationserver/server_test.go index 3cdde09a6..68f15f686 100644 --- a/protocol/pushnotificationserver/server_test.go +++ b/protocol/pushnotificationserver/server_test.go @@ -582,3 +582,91 @@ func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() { s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId) s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList) } + +func (s *ServerSuite) TestPushNotificationMentions() { + existingChatID := []byte("existing-chat-id") + nonExistingChatID := []byte("non-existing-chat-id") + registration := &protobuf.PushNotificationRegistration{ + DeviceToken: "abc", + AccessToken: s.accessToken, + Grant: s.grant, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + InstallationId: s.installationID, + AllowedMentionsChatList: [][]byte{existingChatID}, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + pushNotificationRequest := &protobuf.PushNotificationRequest{ + MessageId: []byte("message-id"), + Requests: []*protobuf.PushNotification{ + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: existingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: nonExistingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + }, + } + + pushNotificationResponse, requestAndRegistrations := s.server.buildPushNotificationRequestResponse(pushNotificationRequest) + s.Require().NotNil(pushNotificationResponse) + s.Require().NotNil(requestAndRegistrations) + + // only one should succeed + s.Require().Len(requestAndRegistrations, 1) +} + +func (s *ServerSuite) TestPushNotificationDisabledMentions() { + existingChatID := []byte("existing-chat-id") + registration := &protobuf.PushNotificationRegistration{ + DeviceToken: "abc", + AccessToken: s.accessToken, + Grant: s.grant, + TokenType: protobuf.PushNotificationRegistration_APN_TOKEN, + BlockMentions: true, + InstallationId: s.installationID, + AllowedMentionsChatList: [][]byte{existingChatID}, + Version: 1, + } + payload, err := proto.Marshal(registration) + s.Require().NoError(err) + + cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader) + s.Require().NoError(err) + response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext) + s.Require().NotNil(response) + s.Require().True(response.Success) + + pushNotificationRequest := &protobuf.PushNotificationRequest{ + MessageId: []byte("message-id"), + Requests: []*protobuf.PushNotification{ + { + AccessToken: s.accessToken, + PublicKey: common.HashPublicKey(&s.key.PublicKey), + ChatId: existingChatID, + InstallationId: s.installationID, + Type: protobuf.PushNotification_MENTION, + }, + }, + } + + pushNotificationResponse, requestAndRegistrations := s.server.buildPushNotificationRequestResponse(pushNotificationRequest) + s.Require().NotNil(pushNotificationResponse) + s.Require().Nil(requestAndRegistrations) +}