From eea7c6b7d72f96b6d3acbd71c6357dcf3a472344 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 8 Aug 2023 15:59:42 +0200 Subject: [PATCH] [PM-2901] Synchronize sends on send creation/update/deletion notification (#2606) * Add sync on send create/update/delete notification * Update send notifications to only sync sends * Fix incorrect notification type in PushNotificationListenerService Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> * Invert if to improve readability * Simplify shouldUpdate logic in SyncUpsertSendAsync * Further simplify SyncService code * Fix if condition in SyncService Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> * Fixed whitespace formatting --------- Co-authored-by: aj-rosado <109146700+aj-rosado@users.noreply.github.com> Co-authored-by: Andre Rosado --- .../PushNotificationListenerService.cs | 18 ++++++ src/Core/Abstractions/ISyncService.cs | 2 + .../Models/Response/NotificationResponse.cs | 7 +++ src/Core/Services/SyncService.cs | 60 +++++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/src/App/Services/PushNotificationListenerService.cs b/src/App/Services/PushNotificationListenerService.cs index b08a43f1c..d51ad85db 100644 --- a/src/App/Services/PushNotificationListenerService.cs +++ b/src/App/Services/PushNotificationListenerService.cs @@ -131,6 +131,24 @@ namespace Bit.App.Services _messagingService.Value.Send("logout"); } break; + case NotificationType.SyncSendCreate: + case NotificationType.SyncSendUpdate: + var sendCreateUpdateMessage = JsonConvert.DeserializeObject( + notification.Payload); + if (isAuthenticated && sendCreateUpdateMessage.UserId == myUserId) + { + await _syncService.Value.SyncUpsertSendAsync(sendCreateUpdateMessage, + notification.Type == NotificationType.SyncSendUpdate); + } + break; + case NotificationType.SyncSendDelete: + var sendDeleteMessage = JsonConvert.DeserializeObject( + notification.Payload); + if (isAuthenticated && sendDeleteMessage.UserId == myUserId) + { + await _syncService.Value.SyncDeleteSendAsync(sendDeleteMessage); + } + break; case NotificationType.AuthRequest: var passwordlessLoginMessage = JsonConvert.DeserializeObject(notification.Payload); diff --git a/src/Core/Abstractions/ISyncService.cs b/src/Core/Abstractions/ISyncService.cs index aeb0205b1..52c686aac 100644 --- a/src/Core/Abstractions/ISyncService.cs +++ b/src/Core/Abstractions/ISyncService.cs @@ -12,8 +12,10 @@ namespace Bit.Core.Abstractions Task SetLastSyncAsync(DateTime date); Task SyncDeleteCipherAsync(SyncCipherNotification notification); Task SyncDeleteFolderAsync(SyncFolderNotification notification); + Task SyncDeleteSendAsync(SyncSendNotification notification); Task SyncUpsertCipherAsync(SyncCipherNotification notification, bool isEdit); Task SyncUpsertFolderAsync(SyncFolderNotification notification, bool isEdit); + Task SyncUpsertSendAsync(SyncSendNotification notification, bool isEdit); // Passwordless code will be moved to an independent service in future techdept Task SyncPasswordlessLoginRequestsAsync(); } diff --git a/src/Core/Models/Response/NotificationResponse.cs b/src/Core/Models/Response/NotificationResponse.cs index ae5405d9b..3976012d5 100644 --- a/src/Core/Models/Response/NotificationResponse.cs +++ b/src/Core/Models/Response/NotificationResponse.cs @@ -34,6 +34,13 @@ namespace Bit.Core.Models.Response public DateTime Date { get; set; } } + public class SyncSendNotification + { + public string Id { get; set; } + public string UserId { get; set; } + public DateTime RevisionDate { get; set; } + } + public class PasswordlessRequestNotification { public string UserId { get; set; } diff --git a/src/Core/Services/SyncService.cs b/src/Core/Services/SyncService.cs index 4351e0f08..843e6f899 100644 --- a/src/Core/Services/SyncService.cs +++ b/src/Core/Services/SyncService.cs @@ -274,6 +274,66 @@ namespace Bit.Core.Services return SyncCompleted(false); } + public async Task SyncUpsertSendAsync(SyncSendNotification notification, bool isEdit) + { + SyncStarted(); + if (!await _stateService.IsAuthenticatedAsync()) + { + return SyncCompleted(false); + } + + try + { + var localSend = await _sendService.GetAsync(notification.Id); + if ((localSend != null && localSend.RevisionDate >= notification.RevisionDate) + || (isEdit && localSend == null) || (!isEdit && localSend != null)) + { + return SyncCompleted(false); + } + + var remoteSend = await _apiService.GetSendAsync(notification.Id); + if (remoteSend != null) + { + var userId = await _stateService.GetActiveUserIdAsync(); + await _sendService.UpsertAsync(new SendData(remoteSend, userId)); + _messagingService.Send("syncedUpsertedSend", new Dictionary + { + ["sendId"] = notification.Id + }); + return SyncCompleted(true); + } + } + catch (ApiException e) + { + if (e.Error != null && e.Error.StatusCode == System.Net.HttpStatusCode.NotFound && isEdit) + { + await _sendService.DeleteAsync(notification.Id); + _messagingService.Send("syncedDeletedSend", new Dictionary + { + ["sendId"] = notification.Id + }); + return SyncCompleted(true); + } + } + + return SyncCompleted(false); + } + + public async Task SyncDeleteSendAsync(SyncSendNotification notification) + { + SyncStarted(); + if (await _stateService.IsAuthenticatedAsync()) + { + await _sendService.DeleteAsync(notification.Id); + _messagingService.Send("syncedDeletedSend", new Dictionary + { + ["sendId"] = notification.Id + }); + return SyncCompleted(true); + } + return SyncCompleted(false); + } + // Helpers private void SyncStarted()