mirror of
https://github.com/bitwarden/server.git
synced 2025-07-17 07:30:59 -05:00
[PM-18555] Main part of notifications refactor (#5757)
* More tests * More tests * Add non-guid tests * Introduce slimmer services * Implement IPushEngine on services * Implement IPushEngine * Fix tests * Format * Switch to `Guid` on `PushSendRequestModel` * Remove TODOs
This commit is contained in:
@ -4,22 +4,22 @@ using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Api;
|
||||
|
||||
public class PushSendRequestModel : IValidatableObject
|
||||
public class PushSendRequestModel<T> : IValidatableObject
|
||||
{
|
||||
public string? UserId { get; set; }
|
||||
public string? OrganizationId { get; set; }
|
||||
public string? DeviceId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public Guid? DeviceId { get; set; }
|
||||
public string? Identifier { get; set; }
|
||||
public required PushType Type { get; set; }
|
||||
public required object Payload { get; set; }
|
||||
public required T Payload { get; set; }
|
||||
public ClientType? ClientType { get; set; }
|
||||
public string? InstallationId { get; set; }
|
||||
public Guid? InstallationId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(UserId) &&
|
||||
string.IsNullOrWhiteSpace(OrganizationId) &&
|
||||
string.IsNullOrWhiteSpace(InstallationId))
|
||||
if (!UserId.HasValue &&
|
||||
!OrganizationId.HasValue &&
|
||||
!InstallationId.HasValue)
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
$"{nameof(UserId)} or {nameof(OrganizationId)} or {nameof(InstallationId)} is required.");
|
||||
|
@ -1,21 +1,17 @@
|
||||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Platform.Push.Internal;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Notification = Bit.Core.NotificationCenter.Entities.Notification;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
@ -26,52 +22,32 @@ namespace Bit.Core.NotificationHub;
|
||||
/// Used by Cloud-Hosted environments.
|
||||
/// Received by Firebase for Android or APNS for iOS.
|
||||
/// </summary>
|
||||
public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
public class NotificationHubPushNotificationService : IPushEngine, IPushRelayer
|
||||
{
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly bool _enableTracing = false;
|
||||
private readonly INotificationHubPool _notificationHubPool;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NotificationHubPushNotificationService(
|
||||
IInstallationDeviceRepository installationDeviceRepository,
|
||||
INotificationHubPool notificationHubPool,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotificationHubPushNotificationService> logger,
|
||||
IGlobalSettings globalSettings,
|
||||
TimeProvider timeProvider)
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_installationDeviceRepository = installationDeviceRepository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_notificationHubPool = notificationHubPool;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_timeProvider = timeProvider;
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
await PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds);
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> 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<Guid>? collectionIds)
|
||||
public async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable<Guid>? collectionIds)
|
||||
{
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
@ -93,311 +69,17 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
CollectionIds = collectionIds,
|
||||
};
|
||||
|
||||
await SendPayloadToUserAsync(cipher.UserId.Value, 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 SendPayloadToUserAsync(folder.UserId, 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 PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.SyncOrganizations);
|
||||
}
|
||||
|
||||
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 = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
await SendPayloadToUserAsync(userId, type, message, excludeCurrentContext);
|
||||
}
|
||||
|
||||
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
|
||||
await PushAsync(new PushNotification<SyncCipherPushNotification>
|
||||
{
|
||||
Id = send.Id,
|
||||
UserId = send.UserId.Value,
|
||||
RevisionDate = send.RevisionDate
|
||||
};
|
||||
|
||||
await SendPayloadToUserAsync(message.UserId, type, message, true);
|
||||
Type = type,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = cipher.UserId.Value,
|
||||
Payload = message,
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushAuthRequestAsync(AuthRequest authRequest)
|
||||
{
|
||||
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
|
||||
}
|
||||
|
||||
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
|
||||
{
|
||||
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
|
||||
}
|
||||
|
||||
public async Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
Guid? installationId = notification.Global && _globalSettings.Installation.Id != Guid.Empty
|
||||
? _globalSettings.Installation.Id
|
||||
: null;
|
||||
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = installationId,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
if (installationId.HasValue)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Invalid global notification id {NotificationId} push notification. No installation id provided.",
|
||||
notification.Id);
|
||||
}
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.Notification, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
Guid? installationId = notification.Global && _globalSettings.Installation.Id != Guid.Empty
|
||||
? _globalSettings.Installation.Id
|
||||
: null;
|
||||
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = installationId,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
ReadDate = notificationStatus.ReadDate,
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
if (installationId.HasValue)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Invalid global notification status id {NotificationId} push notification. No installation id provided.",
|
||||
notification.Id);
|
||||
}
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.NotificationStatus,
|
||||
message, true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification status id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
|
||||
{
|
||||
var message = new AuthRequestPushNotification { Id = authRequest.Id, UserId = authRequest.UserId };
|
||||
|
||||
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToInstallationAsync(Guid installationId, PushType type, object payload,
|
||||
bool excludeCurrentContext, ClientType? clientType = null)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.ToString(), type, payload,
|
||||
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
await SendPayloadToUserAsync(userId.ToString(), type, payload, GetContextIdentifier(excludeCurrentContext),
|
||||
clientType: clientType);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToOrganizationAsync(Guid orgId, PushType type, object payload,
|
||||
bool excludeCurrentContext, ClientType? clientType = null)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(orgId.ToString(), type, payload,
|
||||
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
||||
}
|
||||
|
||||
public async Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.PendingSecurityTasks);
|
||||
}
|
||||
|
||||
public async Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload,
|
||||
string? identifier, string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
var tag = BuildTag($"template:payload && installationId:{installationId}", identifier, clientType);
|
||||
await SendPayloadAsync(tag, type, payload);
|
||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||
{
|
||||
await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
var tag = BuildTag($"template:payload_userId:{SanitizeTagInput(userId)}", identifier, clientType);
|
||||
await SendPayloadAsync(tag, type, payload);
|
||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||
{
|
||||
await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
var tag = BuildTag($"template:payload && organizationId:{SanitizeTagInput(orgId)}", identifier, clientType);
|
||||
await SendPayloadAsync(tag, type, payload);
|
||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||
{
|
||||
await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
var message = new OrganizationStatusPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = organization.Enabled
|
||||
};
|
||||
|
||||
await SendPayloadToOrganizationAsync(organization.Id, PushType.SyncOrganizationStatusChanged, message, false);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization) =>
|
||||
await SendPayloadToOrganizationAsync(
|
||||
organization.Id,
|
||||
PushType.SyncOrganizationCollectionSettingChanged,
|
||||
new OrganizationCollectionManagementPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
LimitCollectionCreation = organization.LimitCollectionCreation,
|
||||
LimitCollectionDeletion = organization.LimitCollectionDeletion,
|
||||
LimitItemDeletion = organization.LimitItemDeletion
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
private string? GetContextIdentifier(bool excludeCurrentContext)
|
||||
{
|
||||
if (!excludeCurrentContext)
|
||||
@ -425,13 +107,73 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
return $"({tag})";
|
||||
}
|
||||
|
||||
private async Task SendPayloadAsync(string tag, PushType type, object payload)
|
||||
public async Task PushAsync<T>(PushNotification<T> pushNotification)
|
||||
where T : class
|
||||
{
|
||||
var initialTag = pushNotification.Target switch
|
||||
{
|
||||
NotificationTarget.User => $"template:payload_userId:{pushNotification.TargetId}",
|
||||
NotificationTarget.Organization => $"template:payload && organizationId:{pushNotification.TargetId}",
|
||||
NotificationTarget.Installation => $"template:payload && installationId:{pushNotification.TargetId}",
|
||||
_ => throw new InvalidOperationException($"Push notification target '{pushNotification.Target}' is not valid."),
|
||||
};
|
||||
|
||||
await PushCoreAsync(
|
||||
initialTag,
|
||||
GetContextIdentifier(pushNotification.ExcludeCurrentContext),
|
||||
pushNotification.Type,
|
||||
pushNotification.ClientType,
|
||||
pushNotification.Payload
|
||||
);
|
||||
}
|
||||
|
||||
public async Task RelayAsync(Guid fromInstallation, RelayedNotification relayedNotification)
|
||||
{
|
||||
// Relayed notifications need identifiers prefixed with the installation they are from and a underscore
|
||||
var initialTag = relayedNotification.Target switch
|
||||
{
|
||||
NotificationTarget.User => $"template:payload_userId:{fromInstallation}_{relayedNotification.TargetId}",
|
||||
NotificationTarget.Organization => $"template:payload && organizationId:{fromInstallation}_{relayedNotification.TargetId}",
|
||||
NotificationTarget.Installation => $"template:payload && installationId:{fromInstallation}",
|
||||
_ => throw new InvalidOperationException($"Invalid Notification target {relayedNotification.Target}"),
|
||||
};
|
||||
|
||||
await PushCoreAsync(
|
||||
initialTag,
|
||||
relayedNotification.Identifier,
|
||||
relayedNotification.Type,
|
||||
relayedNotification.ClientType,
|
||||
relayedNotification.Payload
|
||||
);
|
||||
|
||||
if (relayedNotification.DeviceId.HasValue)
|
||||
{
|
||||
await _installationDeviceRepository.UpsertAsync(
|
||||
new InstallationDeviceEntity(fromInstallation, relayedNotification.DeviceId.Value)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"A related notification of type '{Type}' came through without a device id from installation {Installation}",
|
||||
relayedNotification.Type,
|
||||
fromInstallation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PushCoreAsync<T>(string initialTag, string? contextId, PushType pushType, ClientType? clientType, T payload)
|
||||
{
|
||||
var finalTag = BuildTag(initialTag, contextId, clientType);
|
||||
|
||||
var results = await _notificationHubPool.AllClients.SendTemplateNotificationAsync(
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "type", ((byte)type).ToString() }, { "payload", JsonSerializer.Serialize(payload) }
|
||||
}, tag);
|
||||
{ "type", ((byte)pushType).ToString() },
|
||||
{ "payload", JsonSerializer.Serialize(payload) },
|
||||
},
|
||||
finalTag
|
||||
);
|
||||
|
||||
if (_enableTracing)
|
||||
{
|
||||
@ -444,7 +186,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
|
||||
_logger.LogInformation(
|
||||
"Azure Notification Hub Tracking ID: {Id} | {Type} push notification with {Success} successes and {Failure} failures with a payload of {@Payload} and result of {@Results}",
|
||||
outcome.TrackingId, type, outcome.Success, outcome.Failure, payload, outcome.Results);
|
||||
outcome.TrackingId, pushType, outcome.Success, outcome.Failure, payload, outcome.Results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -17,12 +13,10 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
public class AzureQueuePushNotificationService : IPushEngine
|
||||
{
|
||||
private readonly QueueClient _queueClient;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public AzureQueuePushNotificationService(
|
||||
[FromKeyedServices("notifications")] QueueClient queueClient,
|
||||
@ -33,30 +27,13 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
{
|
||||
_queueClient = queueClient;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
_timeProvider = timeProvider;
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
await PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds);
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> 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<Guid>? collectionIds)
|
||||
public async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable<Guid>? collectionIds)
|
||||
{
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
@ -83,166 +60,6 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
}
|
||||
}
|
||||
|
||||
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 PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.SyncOrganizations);
|
||||
}
|
||||
|
||||
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 = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.Notification, message, true);
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
ReadDate = notificationStatus.ReadDate,
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.NotificationStatus, message, true);
|
||||
}
|
||||
|
||||
public async Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.PendingSecurityTasks);
|
||||
}
|
||||
|
||||
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<T>(PushType type, T payload, bool excludeCurrentContext)
|
||||
{
|
||||
var contextId = GetContextIdentifier(excludeCurrentContext);
|
||||
@ -263,42 +80,9 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
return currentContext?.DeviceIdentifier;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
// Noop
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
public async Task PushAsync<T>(PushNotification<T> pushNotification)
|
||||
where T : class
|
||||
{
|
||||
// Noop
|
||||
return Task.FromResult(0);
|
||||
await SendMessageAsync(pushNotification.Type, pushNotification.Payload, pushNotification.ExcludeCurrentContext);
|
||||
}
|
||||
|
||||
public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
// Noop
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
var message = new OrganizationStatusPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = organization.Enabled
|
||||
};
|
||||
await SendMessageAsync(PushType.SyncOrganizationStatusChanged, message, false);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization) =>
|
||||
await SendMessageAsync(PushType.SyncOrganizationCollectionSettingChanged,
|
||||
new OrganizationCollectionManagementPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
LimitCollectionCreation = organization.LimitCollectionCreation,
|
||||
LimitCollectionDeletion = organization.LimitCollectionDeletion,
|
||||
LimitItemDeletion = organization.LimitItemDeletion
|
||||
}, false);
|
||||
}
|
||||
|
13
src/Core/Platform/Push/Services/IPushEngine.cs
Normal file
13
src/Core/Platform/Push/Services/IPushEngine.cs
Normal file
@ -0,0 +1,13 @@
|
||||
#nullable enable
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Platform.Push;
|
||||
|
||||
public interface IPushEngine
|
||||
{
|
||||
Task PushCipherAsync(Cipher cipher, PushType pushType, IEnumerable<Guid>? collectionIds);
|
||||
|
||||
Task PushAsync<T>(PushNotification<T> pushNotification)
|
||||
where T : class;
|
||||
}
|
@ -2,41 +2,410 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push;
|
||||
|
||||
public interface IPushNotificationService
|
||||
{
|
||||
Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds);
|
||||
Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> collectionIds);
|
||||
Task PushSyncCipherDeleteAsync(Cipher cipher);
|
||||
Task PushSyncFolderCreateAsync(Folder folder);
|
||||
Task PushSyncFolderUpdateAsync(Folder folder);
|
||||
Task PushSyncFolderDeleteAsync(Folder folder);
|
||||
Task PushSyncCiphersAsync(Guid userId);
|
||||
Task PushSyncVaultAsync(Guid userId);
|
||||
Task PushSyncOrganizationsAsync(Guid userId);
|
||||
Task PushSyncOrgKeysAsync(Guid userId);
|
||||
Task PushSyncSettingsAsync(Guid userId);
|
||||
Task PushLogOutAsync(Guid userId, bool excludeCurrentContextFromPush = false);
|
||||
Task PushSyncSendCreateAsync(Send send);
|
||||
Task PushSyncSendUpdateAsync(Send send);
|
||||
Task PushSyncSendDeleteAsync(Send send);
|
||||
Task PushNotificationAsync(Notification notification);
|
||||
Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus);
|
||||
Task PushAuthRequestAsync(AuthRequest authRequest);
|
||||
Task PushAuthRequestResponseAsync(AuthRequest authRequest);
|
||||
Task PushSyncOrganizationStatusAsync(Organization organization);
|
||||
Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization);
|
||||
Guid InstallationId { get; }
|
||||
TimeProvider TimeProvider { get; }
|
||||
ILogger Logger { get; }
|
||||
|
||||
Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null);
|
||||
Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null);
|
||||
Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null);
|
||||
Task PushPendingSecurityTasksAsync(Guid userId);
|
||||
#region Legacy method, to be removed soon.
|
||||
Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
=> PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds);
|
||||
|
||||
Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
=> PushCipherAsync(cipher, PushType.SyncCipherUpdate, collectionIds);
|
||||
|
||||
Task PushSyncCipherDeleteAsync(Cipher cipher)
|
||||
=> PushCipherAsync(cipher, PushType.SyncLoginDelete, null);
|
||||
|
||||
Task PushSyncFolderCreateAsync(Folder folder)
|
||||
=> PushAsync(new PushNotification<SyncFolderPushNotification>
|
||||
{
|
||||
Type = PushType.SyncFolderCreate,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = folder.UserId,
|
||||
Payload = new SyncFolderPushNotification
|
||||
{
|
||||
Id = folder.Id,
|
||||
UserId = folder.UserId,
|
||||
RevisionDate = folder.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
|
||||
Task PushSyncFolderUpdateAsync(Folder folder)
|
||||
=> PushAsync(new PushNotification<SyncFolderPushNotification>
|
||||
{
|
||||
Type = PushType.SyncFolderUpdate,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = folder.UserId,
|
||||
Payload = new SyncFolderPushNotification
|
||||
{
|
||||
Id = folder.Id,
|
||||
UserId = folder.UserId,
|
||||
RevisionDate = folder.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
|
||||
Task PushSyncFolderDeleteAsync(Folder folder)
|
||||
=> PushAsync(new PushNotification<SyncFolderPushNotification>
|
||||
{
|
||||
Type = PushType.SyncFolderDelete,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = folder.UserId,
|
||||
Payload = new SyncFolderPushNotification
|
||||
{
|
||||
Id = folder.Id,
|
||||
UserId = folder.UserId,
|
||||
RevisionDate = folder.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
|
||||
Task PushSyncCiphersAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.SyncCiphers,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushSyncVaultAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.SyncVault,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushSyncOrganizationsAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.SyncOrganizations,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushSyncOrgKeysAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.SyncOrgKeys,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushSyncSettingsAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.SyncSettings,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushLogOutAsync(Guid userId, bool excludeCurrentContextFromPush = false)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.LogOut,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = excludeCurrentContextFromPush,
|
||||
});
|
||||
|
||||
Task PushSyncSendCreateAsync(Send send)
|
||||
{
|
||||
if (send.UserId.HasValue)
|
||||
{
|
||||
return PushAsync(new PushNotification<SyncSendPushNotification>
|
||||
{
|
||||
Type = PushType.SyncSendCreate,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = send.UserId.Value,
|
||||
Payload = new SyncSendPushNotification
|
||||
{
|
||||
Id = send.Id,
|
||||
UserId = send.UserId.Value,
|
||||
RevisionDate = send.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task PushSyncSendUpdateAsync(Send send)
|
||||
{
|
||||
if (send.UserId.HasValue)
|
||||
{
|
||||
return PushAsync(new PushNotification<SyncSendPushNotification>
|
||||
{
|
||||
Type = PushType.SyncSendUpdate,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = send.UserId.Value,
|
||||
Payload = new SyncSendPushNotification
|
||||
{
|
||||
Id = send.Id,
|
||||
UserId = send.UserId.Value,
|
||||
RevisionDate = send.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task PushSyncSendDeleteAsync(Send send)
|
||||
{
|
||||
if (send.UserId.HasValue)
|
||||
{
|
||||
return PushAsync(new PushNotification<SyncSendPushNotification>
|
||||
{
|
||||
Type = PushType.SyncSendDelete,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = send.UserId.Value,
|
||||
Payload = new SyncSendPushNotification
|
||||
{
|
||||
Id = send.Id,
|
||||
UserId = send.UserId.Value,
|
||||
RevisionDate = send.RevisionDate,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? InstallationId : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
};
|
||||
|
||||
NotificationTarget target;
|
||||
Guid targetId;
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
// TODO: Think about this a bit more
|
||||
target = NotificationTarget.Installation;
|
||||
targetId = InstallationId;
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
target = NotificationTarget.User;
|
||||
targetId = notification.UserId.Value;
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
target = NotificationTarget.Organization;
|
||||
targetId = notification.OrganizationId.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("Invalid notification id {NotificationId} push notification", notification.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return PushAsync(new PushNotification<NotificationPushNotification>
|
||||
{
|
||||
Type = PushType.Notification,
|
||||
Target = target,
|
||||
TargetId = targetId,
|
||||
Payload = message,
|
||||
ExcludeCurrentContext = true,
|
||||
ClientType = notification.ClientType,
|
||||
});
|
||||
}
|
||||
|
||||
Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? InstallationId : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
ReadDate = notificationStatus.ReadDate,
|
||||
DeletedDate = notificationStatus.DeletedDate,
|
||||
};
|
||||
|
||||
NotificationTarget target;
|
||||
Guid targetId;
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
// TODO: Think about this a bit more
|
||||
target = NotificationTarget.Installation;
|
||||
targetId = InstallationId;
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
target = NotificationTarget.User;
|
||||
targetId = notification.UserId.Value;
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
target = NotificationTarget.Organization;
|
||||
targetId = notification.OrganizationId.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogWarning("Invalid notification status id {NotificationId} push notification", notification.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return PushAsync(new PushNotification<NotificationPushNotification>
|
||||
{
|
||||
Type = PushType.NotificationStatus,
|
||||
Target = target,
|
||||
TargetId = targetId,
|
||||
Payload = message,
|
||||
ExcludeCurrentContext = true,
|
||||
ClientType = notification.ClientType,
|
||||
});
|
||||
}
|
||||
|
||||
Task PushAuthRequestAsync(AuthRequest authRequest)
|
||||
=> PushAsync(new PushNotification<AuthRequestPushNotification>
|
||||
{
|
||||
Type = PushType.AuthRequest,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = authRequest.UserId,
|
||||
Payload = new AuthRequestPushNotification
|
||||
{
|
||||
Id = authRequest.Id,
|
||||
UserId = authRequest.UserId,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
|
||||
Task PushAuthRequestResponseAsync(AuthRequest authRequest)
|
||||
=> PushAsync(new PushNotification<AuthRequestPushNotification>
|
||||
{
|
||||
Type = PushType.AuthRequestResponse,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = authRequest.UserId,
|
||||
Payload = new AuthRequestPushNotification
|
||||
{
|
||||
Id = authRequest.Id,
|
||||
UserId = authRequest.UserId,
|
||||
},
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
|
||||
Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
=> PushAsync(new PushNotification<OrganizationStatusPushNotification>
|
||||
{
|
||||
Type = PushType.SyncOrganizationStatusChanged,
|
||||
Target = NotificationTarget.Organization,
|
||||
TargetId = organization.Id,
|
||||
Payload = new OrganizationStatusPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = organization.Enabled,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization)
|
||||
=> PushAsync(new PushNotification<OrganizationCollectionManagementPushNotification>
|
||||
{
|
||||
Type = PushType.SyncOrganizationCollectionSettingChanged,
|
||||
Target = NotificationTarget.Organization,
|
||||
TargetId = organization.Id,
|
||||
Payload = new OrganizationCollectionManagementPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
LimitCollectionCreation = organization.LimitCollectionCreation,
|
||||
LimitCollectionDeletion = organization.LimitCollectionDeletion,
|
||||
LimitItemDeletion = organization.LimitItemDeletion,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
|
||||
Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
=> PushAsync(new PushNotification<UserPushNotification>
|
||||
{
|
||||
Type = PushType.PendingSecurityTasks,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = userId,
|
||||
Payload = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = TimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
ExcludeCurrentContext = false,
|
||||
});
|
||||
#endregion
|
||||
|
||||
Task PushCipherAsync(Cipher cipher, PushType pushType, IEnumerable<Guid>? collectionIds);
|
||||
|
||||
Task PushAsync<T>(PushNotification<T> pushNotification)
|
||||
where T : class;
|
||||
}
|
||||
|
44
src/Core/Platform/Push/Services/IPushRelayer.cs
Normal file
44
src/Core/Platform/Push/Services/IPushRelayer.cs
Normal file
@ -0,0 +1,44 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// An object encapsulating the information that is available in a notification
|
||||
/// given to us from a self-hosted installation.
|
||||
/// </summary>
|
||||
public class RelayedNotification
|
||||
{
|
||||
/// <inheritdoc cref="PushNotification{T}.Type"/>
|
||||
public required PushType Type { get; init; }
|
||||
/// <inheritdoc cref="PushNotification{T}.Target"/>
|
||||
public required NotificationTarget Target { get; init; }
|
||||
/// <inheritdoc cref="PushNotification{T}.TargetId"/>
|
||||
public required Guid TargetId { get; init; }
|
||||
/// <inheritdoc cref="PushNotification{T}.Payload"/>
|
||||
public required JsonElement Payload { get; init; }
|
||||
/// <inheritdoc cref="PushNotification{T}.ClientType"/>
|
||||
public required ClientType? ClientType { get; init; }
|
||||
public required Guid? DeviceId { get; init; }
|
||||
public required string? Identifier { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A service for taking a notification that was relayed to us from a self-hosted installation and
|
||||
/// will be injested into our infrastructure so that we can get the notification to devices that require
|
||||
/// cloud interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface should be treated as internal and not consumed by other teams.
|
||||
/// </remarks>
|
||||
public interface IPushRelayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Relays a notification that was received from an authenticated installation into our cloud push notification infrastructure.
|
||||
/// </summary>
|
||||
/// <param name="fromInstallation">The authenticated installation this notification came from.</param>
|
||||
/// <param name="relayedNotification">The information received from the self-hosted installation.</param>
|
||||
Task RelayAsync(Guid fromInstallation, RelayedNotification relayedNotification);
|
||||
}
|
@ -1,202 +1,77 @@
|
||||
#nullable enable
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
public class MultiServicePushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly IEnumerable<IPushNotificationService> _services;
|
||||
private readonly ILogger<MultiServicePushNotificationService> _logger;
|
||||
private readonly IEnumerable<IPushEngine> _services;
|
||||
|
||||
public Guid InstallationId { get; }
|
||||
|
||||
public TimeProvider TimeProvider { get; }
|
||||
|
||||
public ILogger Logger { get; }
|
||||
|
||||
public MultiServicePushNotificationService(
|
||||
[FromKeyedServices("implementation")] IEnumerable<IPushNotificationService> services,
|
||||
IEnumerable<IPushEngine> services,
|
||||
ILogger<MultiServicePushNotificationService> logger,
|
||||
GlobalSettings globalSettings)
|
||||
GlobalSettings globalSettings,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_services = services;
|
||||
|
||||
_logger = logger;
|
||||
_logger.LogInformation("Hub services: {Services}", _services.Count());
|
||||
Logger = logger;
|
||||
Logger.LogInformation("Hub services: {Services}", _services.Count());
|
||||
globalSettings.NotificationHubPool?.NotificationHubs?.ForEach(hub =>
|
||||
{
|
||||
_logger.LogInformation("HubName: {HubName}, EnableSendTracing: {EnableSendTracing}, RegistrationStartDate: {RegistrationStartDate}, RegistrationEndDate: {RegistrationEndDate}", hub.HubName, hub.EnableSendTracing, hub.RegistrationStartDate, hub.RegistrationEndDate);
|
||||
Logger.LogInformation("HubName: {HubName}, EnableSendTracing: {EnableSendTracing}, RegistrationStartDate: {RegistrationStartDate}, RegistrationEndDate: {RegistrationEndDate}", hub.HubName, hub.EnableSendTracing, hub.RegistrationStartDate, hub.RegistrationEndDate);
|
||||
});
|
||||
InstallationId = globalSettings.Installation.Id;
|
||||
TimeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncCipherCreateAsync(cipher, collectionIds));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncCipherUpdateAsync(cipher, collectionIds));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncCipherDeleteAsync(Cipher cipher)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncCipherDeleteAsync(cipher));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderCreateAsync(Folder folder)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncFolderCreateAsync(folder));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderUpdateAsync(Folder folder)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncFolderUpdateAsync(folder));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderDeleteAsync(Folder folder)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncFolderDeleteAsync(folder));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncCiphersAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncCiphersAsync(userId));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncVaultAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncVaultAsync(userId));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncOrganizationsAsync(userId));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrgKeysAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncOrgKeysAsync(userId));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSettingsAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncSettingsAsync(userId));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushLogOutAsync(Guid userId, bool excludeCurrentContext = false)
|
||||
{
|
||||
PushToServices((s) => s.PushLogOutAsync(userId, excludeCurrentContext));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendCreateAsync(Send send)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncSendCreateAsync(send));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendUpdateAsync(Send send)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncSendUpdateAsync(send));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushAuthRequestAsync(AuthRequest authRequest)
|
||||
{
|
||||
PushToServices((s) => s.PushAuthRequestAsync(authRequest));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushAuthRequestResponseAsync(AuthRequest authRequest)
|
||||
{
|
||||
PushToServices((s) => s.PushAuthRequestResponseAsync(authRequest));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendDeleteAsync(Send send)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncSendDeleteAsync(send));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
PushToServices((s) => s.PushSyncOrganizationStatusAsync(organization));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization)
|
||||
{
|
||||
PushToServices(s => s.PushSyncOrganizationCollectionManagementSettingsAsync(organization));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
PushToServices((s) => s.PushNotificationAsync(notification));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
PushToServices((s) => s.PushNotificationStatusAsync(notification, notificationStatus));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
PushToServices((s) =>
|
||||
s.SendPayloadToInstallationAsync(installationId, type, payload, identifier, deviceId, clientType));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
PushToServices((s) => s.SendPayloadToUserAsync(userId, type, payload, identifier, deviceId, clientType));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
PushToServices((s) => s.SendPayloadToOrganizationAsync(orgId, type, payload, identifier, deviceId, clientType));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
PushToServices((s) => s.PushPendingSecurityTasksAsync(userId));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void PushToServices(Func<IPushNotificationService, Task> pushFunc)
|
||||
private Task PushToServices(Func<IPushEngine, Task> pushFunc)
|
||||
{
|
||||
if (!_services.Any())
|
||||
{
|
||||
_logger.LogWarning("No services found to push notification");
|
||||
return;
|
||||
Logger.LogWarning("No services found to push notification");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
var tasks = new List<Task>();
|
||||
#endif
|
||||
|
||||
foreach (var service in _services)
|
||||
{
|
||||
_logger.LogDebug("Pushing notification to service {ServiceName}", service.GetType().Name);
|
||||
Logger.LogDebug("Pushing notification to service {ServiceName}", service.GetType().Name);
|
||||
#if DEBUG
|
||||
var task =
|
||||
#endif
|
||||
pushFunc(service);
|
||||
#if DEBUG
|
||||
tasks.Add(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
return Task.WhenAll(tasks);
|
||||
#else
|
||||
return Task.CompletedTask;
|
||||
#endif
|
||||
}
|
||||
|
||||
public Task PushCipherAsync(Cipher cipher, PushType pushType, IEnumerable<Guid>? collectionIds)
|
||||
{
|
||||
return PushToServices((s) => s.PushCipherAsync(cipher, pushType, collectionIds));
|
||||
}
|
||||
public Task PushAsync<T>(PushNotification<T> pushNotification) where T : class
|
||||
{
|
||||
return PushToServices((s) => s.PushAsync(pushNotification));
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +1,12 @@
|
||||
#nullable enable
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
public class NoopPushNotificationService : IPushNotificationService
|
||||
internal class NoopPushNotificationService : IPushEngine
|
||||
{
|
||||
public Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task PushCipherAsync(Cipher cipher, PushType pushType, IEnumerable<Guid>? collectionIds) => Task.CompletedTask;
|
||||
|
||||
public Task PushSyncCipherDeleteAsync(Cipher cipher)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncCiphersAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderCreateAsync(Folder folder)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderDeleteAsync(Folder folder)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncFolderUpdateAsync(Folder folder)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrgKeysAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSettingsAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncVaultAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushLogOutAsync(Guid userId, bool excludeCurrentContext = false)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendCreateAsync(Send send)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendDeleteAsync(Send send)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncSendUpdateAsync(Send send)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization) => Task.CompletedTask;
|
||||
|
||||
public Task PushAuthRequestAsync(AuthRequest authRequest)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushAuthRequestResponseAsync(AuthRequest authRequest)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushNotificationAsync(Notification notification) => Task.CompletedTask;
|
||||
|
||||
public Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) => Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
public Task PushAsync<T>(PushNotification<T> pushNotification) where T : class => Task.CompletedTask;
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
#nullable enable
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -20,18 +16,15 @@ namespace Bit.Core.Platform.Push;
|
||||
/// Used by Cloud-Hosted environments.
|
||||
/// Received by AzureQueueHostedService message receiver in Notifications project.
|
||||
/// </summary>
|
||||
public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||
public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushEngine
|
||||
{
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NotificationsApiPushNotificationService(
|
||||
IHttpClientFactory httpFactory,
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotificationsApiPushNotificationService> logger,
|
||||
TimeProvider timeProvider)
|
||||
ILogger<NotificationsApiPushNotificationService> logger)
|
||||
: base(
|
||||
httpFactory,
|
||||
globalSettings.BaseServiceUri.InternalNotifications,
|
||||
@ -41,27 +34,10 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
globalSettings.InternalIdentityKey,
|
||||
logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
await PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds);
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> 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<Guid>? collectionIds)
|
||||
public async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable<Guid>? collectionIds)
|
||||
{
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
@ -89,174 +65,6 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
}
|
||||
}
|
||||
|
||||
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 PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.SyncOrganizations);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.LogOut, excludeCurrentContext);
|
||||
}
|
||||
|
||||
private async Task PushUserAsync(Guid userId, PushType type, bool excludeCurrentContext = false)
|
||||
{
|
||||
var message = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = _timeProvider.GetUtcNow().UtcDateTime,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public async Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.Notification, message, true);
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
ReadDate = notificationStatus.ReadDate,
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.NotificationStatus, message, true);
|
||||
}
|
||||
|
||||
public async Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.PendingSecurityTasks);
|
||||
}
|
||||
|
||||
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, false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendMessageAsync<T>(PushType type, T payload, bool excludeCurrentContext)
|
||||
{
|
||||
var contextId = GetContextIdentifier(excludeCurrentContext);
|
||||
@ -276,43 +84,8 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
return currentContext?.DeviceIdentifier;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
// Noop
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
public async Task PushAsync<T>(PushNotification<T> pushNotification) where T : class
|
||||
{
|
||||
// Noop
|
||||
return Task.FromResult(0);
|
||||
await SendMessageAsync(pushNotification.Type, pushNotification.Payload, pushNotification.ExcludeCurrentContext);
|
||||
}
|
||||
|
||||
public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
// Noop
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
var message = new OrganizationStatusPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = organization.Enabled
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.SyncOrganizationStatusChanged, message, false);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization) =>
|
||||
await SendMessageAsync(PushType.SyncOrganizationCollectionSettingChanged,
|
||||
new OrganizationCollectionManagementPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
LimitCollectionCreation = organization.LimitCollectionCreation,
|
||||
LimitCollectionDeletion = organization.LimitCollectionDeletion,
|
||||
LimitItemDeletion = organization.LimitItemDeletion
|
||||
}, false);
|
||||
}
|
||||
|
78
src/Core/Platform/Push/Services/PushNotification.cs
Normal file
78
src/Core/Platform/Push/Services/PushNotification.cs
Normal file
@ -0,0 +1,78 @@
|
||||
#nullable enable
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Platform.Push;
|
||||
|
||||
/// <summary>
|
||||
/// Contains constants for all the available targets for a given notification.
|
||||
/// </summary>
|
||||
public enum NotificationTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// The target for the notification is a single user.
|
||||
/// </summary>
|
||||
User,
|
||||
/// <summary>
|
||||
/// The target for the notification are all the users in an organization.
|
||||
/// </summary>
|
||||
Organization,
|
||||
/// <summary>
|
||||
/// The target for the notification are all the organizations,
|
||||
/// and all the users in that organization for a installation.
|
||||
/// </summary>
|
||||
Installation,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An object containing all the information required for getting a notification
|
||||
/// to an end users device and the information you want available to that device.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the payload. This type is expected to be able to be roundtripped as JSON.</typeparam>
|
||||
public record PushNotification<T>
|
||||
where T : class
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="PushType"/> to be associated with the notification. This is used to route
|
||||
/// the notification to the correct handler on the client side. Be sure to use the correct payload
|
||||
/// type for the associated <see cref="PushType"/>.
|
||||
/// </summary>
|
||||
public required PushType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The target entity type for the notification.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When the target type is <see cref="NotificationTarget.User"/> the <see cref="TargetId"/>
|
||||
/// property is expected to be a users ID. When it is <see cref="NotificationTarget.Organization"/>
|
||||
/// it should be an organizations id. When it is a <see cref="NotificationTarget.Installation"/>
|
||||
/// it should be an installation id.
|
||||
/// </remarks>
|
||||
public required NotificationTarget Target { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The indentifier for the given <see cref="Target"/>.
|
||||
/// </summary>
|
||||
public required Guid TargetId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The payload to be sent with the notification. This object will be JSON serialized.
|
||||
/// </summary>
|
||||
public required T Payload { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When <see langword="true"/> the notification will not include the current context identifier on it, this
|
||||
/// means that the notification may get handled on the device that this notification could have originated from.
|
||||
/// </summary>
|
||||
public required bool ExcludeCurrentContext { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of clients the notification should be sent to, if <see langword="null"/> then
|
||||
/// <see cref="ClientType.All"/> is inferred.
|
||||
/// </summary>
|
||||
public ClientType? ClientType { get; init; }
|
||||
|
||||
internal Guid? GetTargetWhen(NotificationTarget notificationTarget)
|
||||
{
|
||||
return Target == notificationTarget ? TargetId : null;
|
||||
}
|
||||
}
|
@ -1,18 +1,15 @@
|
||||
#nullable enable
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
@ -22,20 +19,18 @@ namespace Bit.Core.Platform.Push.Internal;
|
||||
/// Used by Self-Hosted environments.
|
||||
/// Received by PushController endpoint in Api project.
|
||||
/// </summary>
|
||||
public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||
public class RelayPushNotificationService : BaseIdentityClientService, IPushEngine
|
||||
{
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
|
||||
public RelayPushNotificationService(
|
||||
IHttpClientFactory httpFactory,
|
||||
IDeviceRepository deviceRepository,
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<RelayPushNotificationService> logger,
|
||||
TimeProvider timeProvider)
|
||||
ILogger<RelayPushNotificationService> logger)
|
||||
: base(
|
||||
httpFactory,
|
||||
globalSettings.PushRelayBaseUri,
|
||||
@ -46,27 +41,10 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
logger)
|
||||
{
|
||||
_deviceRepository = deviceRepository;
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
{
|
||||
await PushCipherAsync(cipher, PushType.SyncCipherCreate, collectionIds);
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherUpdateAsync(Cipher cipher, IEnumerable<Guid> 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<Guid>? collectionIds)
|
||||
public async Task PushCipherAsync(Cipher cipher, PushType type, IEnumerable<Guid>? collectionIds)
|
||||
{
|
||||
if (cipher.OrganizationId.HasValue)
|
||||
{
|
||||
@ -87,306 +65,45 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
RevisionDate = cipher.RevisionDate,
|
||||
};
|
||||
|
||||
await SendPayloadToUserAsync(cipher.UserId.Value, 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 SendPayloadToUserAsync(folder.UserId, 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 PushSyncOrganizationsAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.SyncOrganizations);
|
||||
}
|
||||
|
||||
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 = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
await SendPayloadToUserAsync(userId, type, message, excludeCurrentContext);
|
||||
}
|
||||
|
||||
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
|
||||
await PushAsync(new PushNotification<SyncCipherPushNotification>
|
||||
{
|
||||
Id = send.Id,
|
||||
UserId = send.UserId.Value,
|
||||
RevisionDate = send.RevisionDate
|
||||
};
|
||||
|
||||
await SendPayloadToUserAsync(message.UserId, type, message, true);
|
||||
Type = type,
|
||||
Target = NotificationTarget.User,
|
||||
TargetId = cipher.UserId.Value,
|
||||
Payload = message,
|
||||
ExcludeCurrentContext = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushAuthRequestAsync(AuthRequest authRequest)
|
||||
public async Task PushAsync<T>(PushNotification<T> pushNotification)
|
||||
where T : class
|
||||
{
|
||||
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
|
||||
}
|
||||
var deviceIdentifier = _httpContextAccessor.HttpContext
|
||||
?.RequestServices.GetService<ICurrentContext>()
|
||||
?.DeviceIdentifier;
|
||||
|
||||
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
|
||||
{
|
||||
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
|
||||
}
|
||||
Guid? deviceId = null;
|
||||
|
||||
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
|
||||
{
|
||||
var message = new AuthRequestPushNotification { Id = authRequest.Id, UserId = authRequest.UserId };
|
||||
|
||||
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
|
||||
}
|
||||
|
||||
public async Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
if (!string.IsNullOrEmpty(deviceIdentifier))
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(deviceIdentifier);
|
||||
deviceId = device?.Id;
|
||||
}
|
||||
|
||||
var payload = new PushSendRequestModel<T>
|
||||
{
|
||||
Type = pushNotification.Type,
|
||||
UserId = pushNotification.GetTargetWhen(NotificationTarget.User),
|
||||
OrganizationId = pushNotification.GetTargetWhen(NotificationTarget.Organization),
|
||||
InstallationId = pushNotification.GetTargetWhen(NotificationTarget.Installation),
|
||||
Payload = pushNotification.Payload,
|
||||
Identifier = pushNotification.ExcludeCurrentContext ? deviceIdentifier : null,
|
||||
// We set the device id regardless of if they want to exclude the current context or not
|
||||
DeviceId = deviceId,
|
||||
ClientType = pushNotification.ClientType,
|
||||
};
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(PushType.Notification, message, true, notification.ClientType);
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.Notification, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
Priority = notification.Priority,
|
||||
Global = notification.Global,
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
TaskId = notification.TaskId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate,
|
||||
ReadDate = notificationStatus.ReadDate,
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(PushType.NotificationStatus, message, true, notification.ClientType);
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.NotificationStatus, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification status id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
{
|
||||
var message = new OrganizationStatusPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
Enabled = organization.Enabled
|
||||
};
|
||||
|
||||
await SendPayloadToOrganizationAsync(organization.Id, PushType.SyncOrganizationStatusChanged, message, false);
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization) =>
|
||||
await SendPayloadToOrganizationAsync(
|
||||
organization.Id,
|
||||
PushType.SyncOrganizationCollectionSettingChanged,
|
||||
new OrganizationCollectionManagementPushNotification
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
LimitCollectionCreation = organization.LimitCollectionCreation,
|
||||
LimitCollectionDeletion = organization.LimitCollectionDeletion,
|
||||
LimitItemDeletion = organization.LimitItemDeletion
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
public async Task PushPendingSecurityTasksAsync(Guid userId)
|
||||
{
|
||||
await PushUserAsync(userId, PushType.PendingSecurityTasks);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToInstallationAsync(PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
var request = new PushSendRequestModel
|
||||
{
|
||||
InstallationId = _globalSettings.Installation.Id.ToString(),
|
||||
Type = type,
|
||||
Payload = payload,
|
||||
ClientType = clientType
|
||||
};
|
||||
|
||||
await AddCurrentContextAsync(request, excludeCurrentContext);
|
||||
await SendAsync(HttpMethod.Post, "push/send", request);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
var request = new PushSendRequestModel
|
||||
{
|
||||
UserId = userId.ToString(),
|
||||
Type = type,
|
||||
Payload = payload,
|
||||
ClientType = clientType
|
||||
};
|
||||
|
||||
await AddCurrentContextAsync(request, excludeCurrentContext);
|
||||
await SendAsync(HttpMethod.Post, "push/send", request);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToOrganizationAsync(Guid orgId, PushType type, object payload,
|
||||
bool excludeCurrentContext, ClientType? clientType = null)
|
||||
{
|
||||
var request = new PushSendRequestModel
|
||||
{
|
||||
OrganizationId = orgId.ToString(),
|
||||
Type = type,
|
||||
Payload = payload,
|
||||
ClientType = clientType
|
||||
};
|
||||
|
||||
await AddCurrentContextAsync(request, excludeCurrentContext);
|
||||
await SendAsync(HttpMethod.Post, "push/send", request);
|
||||
}
|
||||
|
||||
private async Task AddCurrentContextAsync(PushSendRequestModel request, bool addIdentifier)
|
||||
{
|
||||
var currentContext =
|
||||
_httpContextAccessor.HttpContext?.RequestServices.GetService(typeof(ICurrentContext)) as ICurrentContext;
|
||||
if (!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier))
|
||||
{
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(currentContext.DeviceIdentifier);
|
||||
if (device != null)
|
||||
{
|
||||
request.DeviceId = device.Id.ToString();
|
||||
}
|
||||
|
||||
if (addIdentifier)
|
||||
{
|
||||
request.Identifier = currentContext.DeviceIdentifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
await SendAsync(HttpMethod.Post, "push/send", payload);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user