using System.Text.Json; using Azure.Storage.Queues; using Bit.Core.Auth.Entities; using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Models; using Bit.Core.Settings; using Bit.Core.Tools.Entities; using Bit.Core.Utilities; using Bit.Core.Vault.Entities; using Microsoft.AspNetCore.Http; namespace Bit.Core.Services; public class AzureQueuePushNotificationService : IPushNotificationService { private readonly QueueClient _queueClient; private readonly GlobalSettings _globalSettings; private readonly IHttpContextAccessor _httpContextAccessor; public AzureQueuePushNotificationService( GlobalSettings globalSettings, IHttpContextAccessor httpContextAccessor) { _queueClient = new QueueClient(globalSettings.Notifications.ConnectionString, "notifications"); _globalSettings = globalSettings; _httpContextAccessor = httpContextAccessor; } public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable collectionIds) { await PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds); } public async Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable collectionIds) { await PushCipherAsync(cipher, PushType.SyncCipherUpdate, collectionIds); } public async Task PushSyncCipherDeleteAsync(Cipher cipher) { await PushCipherAsync(cipher, PushType.SyncLoginDelete, null); } private async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable collectionIds) { if (cipher.OrganizationId.HasValue) { var message = new SyncCipherPushNotification { Id = cipher.Id, OrganizationId = cipher.OrganizationId, RevisionDate = cipher.RevisionDate, CollectionIds = collectionIds, }; await SendMessageAsync(type, message, true); } else if (cipher.UserId.HasValue) { var message = new SyncCipherPushNotification { Id = cipher.Id, UserId = cipher.UserId, RevisionDate = cipher.RevisionDate, }; await SendMessageAsync(type, message, true); } } public async Task PushSyncFolderCreateAsync(Folder folder) { await PushFolderAsync(folder, PushType.SyncFolderCreate); } public async Task PushSyncFolderUpdateAsync(Folder folder) { await PushFolderAsync(folder, PushType.SyncFolderUpdate); } public async Task PushSyncFolderDeleteAsync(Folder folder) { await PushFolderAsync(folder, PushType.SyncFolderDelete); } private async Task PushFolderAsync(Folder folder, PushType type) { var message = new SyncFolderPushNotification { Id = folder.Id, UserId = folder.UserId, RevisionDate = folder.RevisionDate }; await SendMessageAsync(type, message, true); } public async Task PushSyncCiphersAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncCiphers); } public async Task PushSyncVaultAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncVault); } public async Task PushSyncOrgKeysAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncOrgKeys); } public async Task PushSyncSettingsAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncSettings); } public async Task PushLogOutAsync(Guid userId, bool excludeCurrentContext = false) { await PushUserAsync(userId, PushType.LogOut, excludeCurrentContext); } private async Task PushUserAsync(Guid userId, PushType type, bool excludeCurrentContext = false) { var message = new UserPushNotification { UserId = userId, Date = DateTime.UtcNow }; await SendMessageAsync(type, message, excludeCurrentContext); } public async Task PushAuthRequestAsync(AuthRequest authRequest) { await PushAuthRequestAsync(authRequest, PushType.AuthRequest); } public async Task PushAuthRequestResponseAsync(AuthRequest authRequest) { await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse); } private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type) { var message = new AuthRequestPushNotification { Id = authRequest.Id, UserId = authRequest.UserId }; await SendMessageAsync(type, message, true); } public async Task PushSyncSendCreateAsync(Send send) { await PushSendAsync(send, PushType.SyncSendCreate); } public async Task PushSyncSendUpdateAsync(Send send) { await PushSendAsync(send, PushType.SyncSendUpdate); } public async Task PushSyncSendDeleteAsync(Send send) { await PushSendAsync(send, PushType.SyncSendDelete); } private async Task PushSendAsync(Send send, PushType type) { if (send.UserId.HasValue) { var message = new SyncSendPushNotification { Id = send.Id, UserId = send.UserId.Value, RevisionDate = send.RevisionDate }; await SendMessageAsync(type, message, true); } } private async Task SendMessageAsync(PushType type, T payload, bool excludeCurrentContext) { var contextId = GetContextIdentifier(excludeCurrentContext); var message = JsonSerializer.Serialize(new PushNotificationData(type, payload, contextId), JsonHelpers.IgnoreWritingNull); await _queueClient.SendMessageAsync(message); } private string GetContextIdentifier(bool excludeCurrentContext) { if (!excludeCurrentContext) { return null; } var currentContext = _httpContextAccessor?.HttpContext?. RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext; return currentContext?.DeviceIdentifier; } public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, string deviceId = null) { // Noop return Task.FromResult(0); } public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, string deviceId = null) { // Noop return Task.FromResult(0); } }