mirror of
https://github.com/bitwarden/server
synced 2025-12-15 07:43:54 +00:00
PM-15084: Push notifications to installation id.
This enables the Notification Center created global notifications to be sent to affected devices of the same server installation. All clients connected to any of the server instance of that installation id would receive it. This is useful for notifying all clients of an installation about upcoming maintenance. This works both for Self-Hosted, but also for Cloud, assuming an installation id is set.
This commit is contained in:
@@ -18,14 +18,14 @@ public class PushController : Controller
|
|||||||
private readonly IPushNotificationService _pushNotificationService;
|
private readonly IPushNotificationService _pushNotificationService;
|
||||||
private readonly IWebHostEnvironment _environment;
|
private readonly IWebHostEnvironment _environment;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
public PushController(
|
public PushController(
|
||||||
IPushRegistrationService pushRegistrationService,
|
IPushRegistrationService pushRegistrationService,
|
||||||
IPushNotificationService pushNotificationService,
|
IPushNotificationService pushNotificationService,
|
||||||
IWebHostEnvironment environment,
|
IWebHostEnvironment environment,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings)
|
IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_environment = environment;
|
_environment = environment;
|
||||||
@@ -35,22 +35,23 @@ public class PushController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
[HttpPost("register")]
|
||||||
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
|
public async Task RegisterAsync([FromBody] PushRegistrationRequestModel model)
|
||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
|
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
|
||||||
Prefix(model.UserId), Prefix(model.Identifier), model.Type, model.OrganizationIds.Select(Prefix));
|
Prefix(model.UserId), Prefix(model.Identifier), model.Type, model.OrganizationIds.Select(Prefix),
|
||||||
|
model.InstallationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("delete")]
|
[HttpPost("delete")]
|
||||||
public async Task PostDelete([FromBody] PushDeviceRequestModel model)
|
public async Task DeleteAsync([FromBody] PushDeviceRequestModel model)
|
||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(model.Id));
|
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(model.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("add-organization")]
|
[HttpPut("add-organization")]
|
||||||
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
|
public async Task AddOrganizationAsync([FromBody] PushUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
|
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
|
||||||
@@ -59,7 +60,7 @@ public class PushController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("delete-organization")]
|
[HttpPut("delete-organization")]
|
||||||
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
|
public async Task DeleteOrganizationAsync([FromBody] PushUpdateRequestModel model)
|
||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
|
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
|
||||||
@@ -68,11 +69,22 @@ public class PushController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("send")]
|
[HttpPost("send")]
|
||||||
public async Task PostSend([FromBody] PushSendRequestModel model)
|
public async Task SendAsync([FromBody] PushSendRequestModel model)
|
||||||
{
|
{
|
||||||
CheckUsage();
|
CheckUsage();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.UserId))
|
if (!string.IsNullOrWhiteSpace(model.InstallationId))
|
||||||
|
{
|
||||||
|
if (_currentContext.InstallationId!.Value.ToString() != model.InstallationId!)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("InstallationId does not match current context.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _pushNotificationService.SendPayloadToInstallationAsync(
|
||||||
|
_currentContext.InstallationId.Value.ToString(), model.Type, model.Payload, Prefix(model.Identifier),
|
||||||
|
Prefix(model.DeviceId), model.ClientType);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||||
{
|
{
|
||||||
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
||||||
model.Type, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId), model.ClientType);
|
model.Type, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId), model.ClientType);
|
||||||
@@ -91,7 +103,7 @@ public class PushController : Controller
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"{_currentContext.InstallationId.Value}_{value}";
|
return $"{_currentContext.InstallationId!.Value}_{value}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckUsage()
|
private void CheckUsage()
|
||||||
|
|||||||
@@ -5,15 +5,11 @@ namespace Bit.Core.Models.Api;
|
|||||||
|
|
||||||
public class PushRegistrationRequestModel
|
public class PushRegistrationRequestModel
|
||||||
{
|
{
|
||||||
[Required]
|
[Required] public string DeviceId { get; set; }
|
||||||
public string DeviceId { get; set; }
|
[Required] public string PushToken { get; set; }
|
||||||
[Required]
|
[Required] public string UserId { get; set; }
|
||||||
public string PushToken { get; set; }
|
[Required] public DeviceType Type { get; set; }
|
||||||
[Required]
|
[Required] public string Identifier { get; set; }
|
||||||
public string UserId { get; set; }
|
|
||||||
[Required]
|
|
||||||
public DeviceType Type { get; set; }
|
|
||||||
[Required]
|
|
||||||
public string Identifier { get; set; }
|
|
||||||
public IEnumerable<string> OrganizationIds { get; set; }
|
public IEnumerable<string> OrganizationIds { get; set; }
|
||||||
|
public string InstallationId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ public class PushSendRequestModel : IValidatableObject
|
|||||||
public required PushType Type { get; set; }
|
public required PushType Type { get; set; }
|
||||||
public required object Payload { get; set; }
|
public required object Payload { get; set; }
|
||||||
public ClientType? ClientType { get; set; }
|
public ClientType? ClientType { get; set; }
|
||||||
|
public string? InstallationId { get; set; }
|
||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(UserId) && string.IsNullOrWhiteSpace(OrganizationId))
|
if (string.IsNullOrWhiteSpace(UserId) &&
|
||||||
|
string.IsNullOrWhiteSpace(OrganizationId) &&
|
||||||
|
string.IsNullOrWhiteSpace(InstallationId))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult($"{nameof(UserId)} or {nameof(OrganizationId)} is required.");
|
yield return new ValidationResult(
|
||||||
|
$"{nameof(UserId)} or {nameof(OrganizationId)} or {nameof(InstallationId)} is required.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public class SyncNotificationPushNotification
|
|||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
public Guid? OrganizationId { get; set; }
|
public Guid? OrganizationId { get; set; }
|
||||||
|
public Guid? InstallationId { get; set; }
|
||||||
public ClientType ClientType { get; set; }
|
public ClientType ClientType { get; set; }
|
||||||
public DateTime RevisionDate { get; set; }
|
public DateTime RevisionDate { get; set; }
|
||||||
public DateTime? ReadDate { get; set; }
|
public DateTime? ReadDate { get; set; }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Bit.Core.Models.Data;
|
|||||||
using Bit.Core.NotificationCenter.Entities;
|
using Bit.Core.NotificationCenter.Entities;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Tools.Entities;
|
using Bit.Core.Tools.Entities;
|
||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@@ -17,6 +18,11 @@ using Notification = Bit.Core.NotificationCenter.Entities.Notification;
|
|||||||
|
|
||||||
namespace Bit.Core.NotificationHub;
|
namespace Bit.Core.NotificationHub;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends mobile push notifications to the Azure Notification Hub.
|
||||||
|
/// Used by Cloud-Hosted environments.
|
||||||
|
/// Received by Firebase for Android or APNS for iOS.
|
||||||
|
/// </summary>
|
||||||
public class NotificationHubPushNotificationService : IPushNotificationService
|
public class NotificationHubPushNotificationService : IPushNotificationService
|
||||||
{
|
{
|
||||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||||
@@ -24,17 +30,20 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
|||||||
private readonly bool _enableTracing = false;
|
private readonly bool _enableTracing = false;
|
||||||
private readonly INotificationHubPool _notificationHubPool;
|
private readonly INotificationHubPool _notificationHubPool;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
public NotificationHubPushNotificationService(
|
public NotificationHubPushNotificationService(
|
||||||
IInstallationDeviceRepository installationDeviceRepository,
|
IInstallationDeviceRepository installationDeviceRepository,
|
||||||
INotificationHubPool notificationHubPool,
|
INotificationHubPool notificationHubPool,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
ILogger<NotificationsApiPushNotificationService> logger)
|
ILogger<NotificationHubPushNotificationService> logger,
|
||||||
|
IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_installationDeviceRepository = installationDeviceRepository;
|
_installationDeviceRepository = installationDeviceRepository;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_notificationHubPool = notificationHubPool;
|
_notificationHubPool = notificationHubPool;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||||
@@ -184,48 +193,70 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
|||||||
|
|
||||||
public async Task PushSyncNotificationCreateAsync(Notification notification)
|
public async Task PushSyncNotificationCreateAsync(Notification notification)
|
||||||
{
|
{
|
||||||
|
Guid? installationId = notification.Global && _globalSettings.Installation.Id != default
|
||||||
|
? _globalSettings.Installation.Id
|
||||||
|
: null;
|
||||||
|
|
||||||
var message = new SyncNotificationPushNotification
|
var message = new SyncNotificationPushNotification
|
||||||
{
|
{
|
||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = installationId,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate
|
RevisionDate = notification.RevisionDate
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.UserId.HasValue)
|
if (notification.Global && installationId.HasValue)
|
||||||
|
{
|
||||||
|
await SendPayloadToInstallationAsync(installationId.Value, PushType.SyncNotificationCreate, message, true,
|
||||||
|
notification.ClientType);
|
||||||
|
}
|
||||||
|
else if (notification.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true,
|
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true,
|
||||||
notification.ClientType);
|
notification.ClientType);
|
||||||
}
|
}
|
||||||
else if (notification.OrganizationId.HasValue)
|
else if (notification.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate, message,
|
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate,
|
||||||
|
message,
|
||||||
true, notification.ClientType);
|
true, notification.ClientType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus)
|
public async Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus)
|
||||||
{
|
{
|
||||||
|
Guid? installationId = notification.Global && _globalSettings.Installation.Id != default
|
||||||
|
? _globalSettings.Installation.Id
|
||||||
|
: null;
|
||||||
|
|
||||||
var message = new SyncNotificationPushNotification
|
var message = new SyncNotificationPushNotification
|
||||||
{
|
{
|
||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = installationId,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate,
|
RevisionDate = notification.RevisionDate,
|
||||||
ReadDate = notificationStatus?.ReadDate,
|
ReadDate = notificationStatus?.ReadDate,
|
||||||
DeletedDate = notificationStatus?.DeletedDate
|
DeletedDate = notificationStatus?.DeletedDate
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.UserId.HasValue)
|
if (notification.Global && installationId.HasValue)
|
||||||
|
{
|
||||||
|
await SendPayloadToInstallationAsync(installationId.Value, PushType.SyncNotificationUpdate, message, true,
|
||||||
|
notification.ClientType);
|
||||||
|
}
|
||||||
|
else if (notification.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true,
|
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true,
|
||||||
notification.ClientType);
|
notification.ClientType);
|
||||||
}
|
}
|
||||||
else if (notification.OrganizationId.HasValue)
|
else if (notification.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate, message,
|
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate,
|
||||||
|
message,
|
||||||
true, notification.ClientType);
|
true, notification.ClientType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,6 +268,13 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
|||||||
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
|
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,
|
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||||
ClientType? clientType = null)
|
ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
@@ -251,6 +289,17 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
|||||||
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
string identifier, DeviceType type, IEnumerable<string> organizationIds, string installationId)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(pushToken))
|
if (string.IsNullOrWhiteSpace(pushToken))
|
||||||
{
|
{
|
||||||
@@ -50,6 +50,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|||||||
installation.Tags.Add($"organizationId:{organizationId}");
|
installation.Tags.Add($"organizationId:{organizationId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(installationId))
|
||||||
|
{
|
||||||
|
installation.Tags.Add($"installationId:{installationId}");
|
||||||
|
}
|
||||||
|
|
||||||
string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null;
|
string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null;
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
@@ -80,11 +85,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier, clientType,
|
BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier, clientType,
|
||||||
organizationIdsList);
|
organizationIdsList, installationId);
|
||||||
BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier, clientType,
|
BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier, clientType,
|
||||||
organizationIdsList);
|
organizationIdsList, installationId);
|
||||||
BuildInstallationTemplate(installation, "badgeMessage", badgeMessageTemplate ?? messageTemplate,
|
BuildInstallationTemplate(installation, "badgeMessage", badgeMessageTemplate ?? messageTemplate,
|
||||||
userId, identifier, clientType, organizationIdsList);
|
userId, identifier, clientType, organizationIdsList, installationId);
|
||||||
|
|
||||||
await ClientFor(GetComb(deviceId)).CreateOrUpdateInstallationAsync(installation);
|
await ClientFor(GetComb(deviceId)).CreateOrUpdateInstallationAsync(installation);
|
||||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||||
@@ -94,7 +99,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody,
|
private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody,
|
||||||
string userId, string identifier, ClientType clientType, List<string> organizationIds)
|
string userId, string identifier, ClientType clientType, List<string> organizationIds, string installationId)
|
||||||
{
|
{
|
||||||
if (templateBody == null)
|
if (templateBody == null)
|
||||||
{
|
{
|
||||||
@@ -122,6 +127,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|||||||
template.Tags.Add($"organizationId:{organizationId}");
|
template.Tags.Add($"organizationId:{organizationId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(installationId))
|
||||||
|
{
|
||||||
|
template.Tags.Add($"installationId:{installationId}");
|
||||||
|
}
|
||||||
|
|
||||||
installation.Templates.Add(fullTemplateId, template);
|
installation.Templates.Add(fullTemplateId, template);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ public interface IPushNotificationService
|
|||||||
Task PushAuthRequestAsync(AuthRequest authRequest);
|
Task PushAuthRequestAsync(AuthRequest authRequest);
|
||||||
Task PushAuthRequestResponseAsync(AuthRequest authRequest);
|
Task PushAuthRequestResponseAsync(AuthRequest authRequest);
|
||||||
|
|
||||||
|
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,
|
Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null);
|
string? deviceId = null, ClientType? clientType = null);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Bit.Core.Services;
|
|||||||
public interface IPushRegistrationService
|
public interface IPushRegistrationService
|
||||||
{
|
{
|
||||||
Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||||
string identifier, DeviceType type, IEnumerable<string> organizationIds);
|
string identifier, DeviceType type, IEnumerable<string> organizationIds, string installationId);
|
||||||
Task DeleteRegistrationAsync(string deviceId);
|
Task DeleteRegistrationAsync(string deviceId);
|
||||||
Task AddUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
Task AddUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
||||||
Task DeleteUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
Task DeleteUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.Context;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
using Bit.Core.NotificationCenter.Entities;
|
using Bit.Core.NotificationCenter.Entities;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Tools.Entities;
|
using Bit.Core.Tools.Entities;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Core.Vault.Entities;
|
using Bit.Core.Vault.Entities;
|
||||||
@@ -18,13 +19,16 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
|||||||
{
|
{
|
||||||
private readonly QueueClient _queueClient;
|
private readonly QueueClient _queueClient;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
public AzureQueuePushNotificationService(
|
public AzureQueuePushNotificationService(
|
||||||
[FromKeyedServices("notifications")] QueueClient queueClient,
|
[FromKeyedServices("notifications")] QueueClient queueClient,
|
||||||
IHttpContextAccessor httpContextAccessor)
|
IHttpContextAccessor httpContextAccessor,
|
||||||
|
IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_queueClient = queueClient;
|
_queueClient = queueClient;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||||
@@ -172,6 +176,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate
|
RevisionDate = notification.RevisionDate
|
||||||
};
|
};
|
||||||
@@ -186,6 +191,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate,
|
RevisionDate = notification.RevisionDate,
|
||||||
ReadDate = notificationStatus?.ReadDate,
|
ReadDate = notificationStatus?.ReadDate,
|
||||||
@@ -230,6 +236,11 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
|||||||
return currentContext?.DeviceIdentifier;
|
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,
|
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
@@ -12,15 +13,18 @@ public class DeviceService : IDeviceService
|
|||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
private readonly IPushRegistrationService _pushRegistrationService;
|
private readonly IPushRegistrationService _pushRegistrationService;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
public DeviceService(
|
public DeviceService(
|
||||||
IDeviceRepository deviceRepository,
|
IDeviceRepository deviceRepository,
|
||||||
IPushRegistrationService pushRegistrationService,
|
IPushRegistrationService pushRegistrationService,
|
||||||
IOrganizationUserRepository organizationUserRepository)
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
_pushRegistrationService = pushRegistrationService;
|
_pushRegistrationService = pushRegistrationService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Device device)
|
public async Task SaveAsync(Device device)
|
||||||
@@ -41,7 +45,8 @@ public class DeviceService : IDeviceService
|
|||||||
.Select(ou => ou.OrganizationId.ToString());
|
.Select(ou => ou.OrganizationId.ToString());
|
||||||
|
|
||||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(device.PushToken, device.Id.ToString(),
|
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(device.PushToken, device.Id.ToString(),
|
||||||
device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString);
|
device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString,
|
||||||
|
_globalSettings.Installation.Id.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ClearTokenAsync(Device device)
|
public async Task ClearTokenAsync(Device device)
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ public class MultiServicePushNotificationService : IPushNotificationService
|
|||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends non-mobile push notifications to the Azure Queue Api, later received by Notifications Api.
|
||||||
|
/// Used by Cloud-Hosted environments.
|
||||||
|
/// Received by AzureQueueHostedService message receiver in Notifications project.
|
||||||
|
/// </summary>
|
||||||
public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||||
{
|
{
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public NotificationsApiPushNotificationService(
|
public NotificationsApiPushNotificationService(
|
||||||
@@ -30,6 +36,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
|||||||
globalSettings.InternalIdentityKey,
|
globalSettings.InternalIdentityKey,
|
||||||
logger)
|
logger)
|
||||||
{
|
{
|
||||||
|
_globalSettings = globalSettings;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +186,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate
|
RevisionDate = notification.RevisionDate
|
||||||
};
|
};
|
||||||
@@ -193,6 +201,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate,
|
RevisionDate = notification.RevisionDate,
|
||||||
ReadDate = notificationStatus?.ReadDate,
|
ReadDate = notificationStatus?.ReadDate,
|
||||||
@@ -236,6 +245,11 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
|||||||
return currentContext?.DeviceIdentifier;
|
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,
|
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,9 +15,15 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends mobile push notifications to the Bitwarden Cloud API, then relayed to Azure Notification Hub.
|
||||||
|
/// Used by Self-Hosted environments.
|
||||||
|
/// Received by PushController endpoint in Api project.
|
||||||
|
/// </summary>
|
||||||
public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||||
{
|
{
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public RelayPushNotificationService(
|
public RelayPushNotificationService(
|
||||||
@@ -36,6 +42,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
|||||||
logger)
|
logger)
|
||||||
{
|
{
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,18 +204,25 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate
|
RevisionDate = notification.RevisionDate
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.UserId.HasValue)
|
if (notification.Global)
|
||||||
|
{
|
||||||
|
await SendPayloadToInstallationAsync(PushType.SyncNotificationCreate, message, true,
|
||||||
|
notification.ClientType);
|
||||||
|
}
|
||||||
|
else if (notification.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true,
|
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationCreate, message, true,
|
||||||
notification.ClientType);
|
notification.ClientType);
|
||||||
}
|
}
|
||||||
else if (notification.OrganizationId.HasValue)
|
else if (notification.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate, message,
|
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationCreate,
|
||||||
|
message,
|
||||||
true, notification.ClientType);
|
true, notification.ClientType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,24 +234,45 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
|||||||
Id = notification.Id,
|
Id = notification.Id,
|
||||||
UserId = notification.UserId,
|
UserId = notification.UserId,
|
||||||
OrganizationId = notification.OrganizationId,
|
OrganizationId = notification.OrganizationId,
|
||||||
|
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||||
ClientType = notification.ClientType,
|
ClientType = notification.ClientType,
|
||||||
RevisionDate = notification.RevisionDate,
|
RevisionDate = notification.RevisionDate,
|
||||||
ReadDate = notificationStatus?.ReadDate,
|
ReadDate = notificationStatus?.ReadDate,
|
||||||
DeletedDate = notificationStatus?.DeletedDate
|
DeletedDate = notificationStatus?.DeletedDate
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.UserId.HasValue)
|
if (notification.Global)
|
||||||
|
{
|
||||||
|
await SendPayloadToInstallationAsync(PushType.SyncNotificationUpdate, message, true,
|
||||||
|
notification.ClientType);
|
||||||
|
}
|
||||||
|
else if (notification.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true,
|
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationUpdate, message, true,
|
||||||
notification.ClientType);
|
notification.ClientType);
|
||||||
}
|
}
|
||||||
else if (notification.OrganizationId.HasValue)
|
else if (notification.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate, message,
|
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationUpdate,
|
||||||
true, notification.ClientType);
|
message, true, notification.ClientType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||||
ClientType? clientType = null)
|
ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
@@ -287,6 +322,10 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
string identifier, DeviceType type, IEnumerable<string> organizationIds, string installationId)
|
||||||
{
|
{
|
||||||
var requestModel = new PushRegistrationRequestModel
|
var requestModel = new PushRegistrationRequestModel
|
||||||
{
|
{
|
||||||
@@ -34,7 +34,8 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi
|
|||||||
PushToken = pushToken,
|
PushToken = pushToken,
|
||||||
Type = type,
|
Type = type,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
OrganizationIds = organizationIds
|
OrganizationIds = organizationIds,
|
||||||
|
InstallationId = installationId
|
||||||
};
|
};
|
||||||
await SendAsync(HttpMethod.Post, "push/register", requestModel);
|
await SendAsync(HttpMethod.Post, "push/register", requestModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ public class NoopPushNotificationService : IPushNotificationService
|
|||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||||
string? deviceId = null, ClientType? clientType = null)
|
string? deviceId = null, ClientType? clientType = null)
|
||||||
{
|
{
|
||||||
@@ -108,5 +111,6 @@ public class NoopPushNotificationService : IPushNotificationService
|
|||||||
|
|
||||||
public Task PushSyncNotificationCreateAsync(Notification notification) => Task.CompletedTask;
|
public Task PushSyncNotificationCreateAsync(Notification notification) => Task.CompletedTask;
|
||||||
|
|
||||||
public Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) => Task.CompletedTask;
|
public Task PushSyncNotificationUpdateAsync(Notification notification, NotificationStatus? notificationStatus) =>
|
||||||
|
Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class NoopPushRegistrationService : IPushRegistrationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
public Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
string identifier, DeviceType type, IEnumerable<string> organizationIds, string installationId)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,13 @@ public static class HubHelpers
|
|||||||
var syncNotification =
|
var syncNotification =
|
||||||
JsonSerializer.Deserialize<PushNotificationData<SyncNotificationPushNotification>>(
|
JsonSerializer.Deserialize<PushNotificationData<SyncNotificationPushNotification>>(
|
||||||
notificationJson, _deserializerOptions);
|
notificationJson, _deserializerOptions);
|
||||||
if (syncNotification.Payload.UserId.HasValue)
|
if (syncNotification.Payload.InstallationId.HasValue)
|
||||||
|
{
|
||||||
|
await hubContext.Clients.Group(NotificationsHub.GetInstallationGroup(
|
||||||
|
syncNotification.Payload.InstallationId.Value, syncNotification.Payload.ClientType))
|
||||||
|
.SendAsync(_receiveMessageMethod, syncNotification, cancellationToken);
|
||||||
|
}
|
||||||
|
else if (syncNotification.Payload.UserId.HasValue)
|
||||||
{
|
{
|
||||||
if (syncNotification.Payload.ClientType == ClientType.All)
|
if (syncNotification.Payload.ClientType == ClientType.All)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
|||||||
await Groups.AddToGroupAsync(Context.ConnectionId, GetUserGroup(currentContext.UserId.Value, clientType));
|
await Groups.AddToGroupAsync(Context.ConnectionId, GetUserGroup(currentContext.UserId.Value, clientType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentContext.InstallationId.HasValue)
|
||||||
|
{
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId,
|
||||||
|
GetInstallationGroup(currentContext.InstallationId.Value));
|
||||||
|
if (clientType != ClientType.All)
|
||||||
|
{
|
||||||
|
await Groups.AddToGroupAsync(Context.ConnectionId,
|
||||||
|
GetInstallationGroup(currentContext.InstallationId.Value, clientType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (currentContext.Organizations != null)
|
if (currentContext.Organizations != null)
|
||||||
{
|
{
|
||||||
foreach (var org in currentContext.Organizations)
|
foreach (var org in currentContext.Organizations)
|
||||||
@@ -57,6 +68,17 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
|||||||
GetUserGroup(currentContext.UserId.Value, clientType));
|
GetUserGroup(currentContext.UserId.Value, clientType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentContext.InstallationId.HasValue)
|
||||||
|
{
|
||||||
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId,
|
||||||
|
GetInstallationGroup(currentContext.InstallationId.Value));
|
||||||
|
if (clientType != ClientType.All)
|
||||||
|
{
|
||||||
|
await Groups.RemoveFromGroupAsync(Context.ConnectionId,
|
||||||
|
GetInstallationGroup(currentContext.InstallationId.Value, clientType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (currentContext.Organizations != null)
|
if (currentContext.Organizations != null)
|
||||||
{
|
{
|
||||||
foreach (var org in currentContext.Organizations)
|
foreach (var org in currentContext.Organizations)
|
||||||
@@ -73,6 +95,13 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
|||||||
await base.OnDisconnectedAsync(exception);
|
await base.OnDisconnectedAsync(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetInstallationGroup(Guid installationId, ClientType? clientType = null)
|
||||||
|
{
|
||||||
|
return clientType is null or ClientType.All
|
||||||
|
? $"Installation_{installationId}"
|
||||||
|
: $"Installation_ClientType_{installationId}_{clientType}";
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetUserGroup(Guid userId, ClientType clientType)
|
public static string GetUserGroup(Guid userId, ClientType clientType)
|
||||||
{
|
{
|
||||||
return $"UserClientType_{userId}_{clientType}";
|
return $"UserClientType_{userId}_{clientType}";
|
||||||
|
|||||||
@@ -272,9 +272,13 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddSingleton<IPushNotificationService, MultiServicePushNotificationService>();
|
services.AddSingleton<IPushNotificationService, MultiServicePushNotificationService>();
|
||||||
if (globalSettings.SelfHosted)
|
if (globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
|
if (globalSettings.Installation.Id == default)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Installation Id must be set for self-hosted installations.");
|
||||||
|
}
|
||||||
|
|
||||||
if (CoreHelpers.SettingHasValue(globalSettings.PushRelayBaseUri) &&
|
if (CoreHelpers.SettingHasValue(globalSettings.PushRelayBaseUri) &&
|
||||||
globalSettings.Installation?.Id != null &&
|
CoreHelpers.SettingHasValue(globalSettings.Installation.Key))
|
||||||
CoreHelpers.SettingHasValue(globalSettings.Installation?.Key))
|
|
||||||
{
|
{
|
||||||
services.AddKeyedSingleton<IPushNotificationService, RelayPushNotificationService>("implementation");
|
services.AddKeyedSingleton<IPushNotificationService, RelayPushNotificationService>("implementation");
|
||||||
services.AddSingleton<IPushRegistrationService, RelayPushRegistrationService>();
|
services.AddSingleton<IPushRegistrationService, RelayPushRegistrationService>();
|
||||||
@@ -290,7 +294,7 @@ public static class ServiceCollectionExtensions
|
|||||||
services.AddKeyedSingleton<IPushNotificationService, NotificationsApiPushNotificationService>("implementation");
|
services.AddKeyedSingleton<IPushNotificationService, NotificationsApiPushNotificationService>("implementation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!globalSettings.SelfHosted)
|
else
|
||||||
{
|
{
|
||||||
services.AddSingleton<INotificationHubPool, NotificationHubPool>();
|
services.AddSingleton<INotificationHubPool, NotificationHubPool>();
|
||||||
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
|
|
||||||
|
|
||||||
namespace Bit.Api.Test.Controllers;
|
namespace Bit.Api.Test.Controllers;
|
||||||
|
|
||||||
@@ -24,18 +24,19 @@ public class PushControllerTests
|
|||||||
public async Task SendAsync_InstallationIdNotSetOrSelfHosted_BadRequest(bool haveInstallationId, bool selfHosted,
|
public async Task SendAsync_InstallationIdNotSetOrSelfHosted_BadRequest(bool haveInstallationId, bool selfHosted,
|
||||||
SutProvider<PushController> sutProvider, Guid installationId, Guid userId, Guid organizationId)
|
SutProvider<PushController> sutProvider, Guid installationId, Guid userId, Guid organizationId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<GlobalSettings>().SelfHosted = selfHosted;
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = selfHosted;
|
||||||
if (haveInstallationId)
|
if (haveInstallationId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
sutProvider.Sut.PostSend(new PushSendRequestModel
|
sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
{
|
{
|
||||||
Type = PushType.SyncNotificationCreate,
|
Type = PushType.SyncNotificationCreate,
|
||||||
UserId = userId.ToString(),
|
UserId = userId.ToString(),
|
||||||
OrganizationId = organizationId.ToString(),
|
OrganizationId = organizationId.ToString(),
|
||||||
|
InstallationId = installationId.ToString(),
|
||||||
Payload = "test-payload"
|
Payload = "test-payload"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -47,21 +48,25 @@ public class PushControllerTests
|
|||||||
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToInstallationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task SendAsync_UserIdAndOrganizationIdEmpty_NoPushNotificationSent(
|
public async Task SendAsync_UserIdAndOrganizationIdAndInstallationIdEmpty_NoPushNotificationSent(
|
||||||
SutProvider<PushController> sutProvider, Guid installationId)
|
SutProvider<PushController> sutProvider, Guid installationId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
await sutProvider.Sut.PostSend(new PushSendRequestModel
|
await sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
{
|
{
|
||||||
Type = PushType.SyncNotificationCreate,
|
Type = PushType.SyncNotificationCreate,
|
||||||
UserId = null,
|
UserId = null,
|
||||||
OrganizationId = null,
|
OrganizationId = null,
|
||||||
|
InstallationId = null,
|
||||||
Payload = "test-payload"
|
Payload = "test-payload"
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -71,33 +76,30 @@ public class PushControllerTests
|
|||||||
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToInstallationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(true, true, false)]
|
[RepeatingPatternBitAutoData([false, true], [false, true], [false, true])]
|
||||||
[BitAutoData(true, true, true)]
|
|
||||||
[BitAutoData(true, false, true)]
|
|
||||||
[BitAutoData(true, false, false)]
|
|
||||||
[BitAutoData(false, true, true)]
|
|
||||||
[BitAutoData(false, true, false)]
|
|
||||||
[BitAutoData(false, false, true)]
|
|
||||||
[BitAutoData(false, false, false)]
|
|
||||||
public async Task SendAsync_UserIdSet_SendPayloadToUserAsync(bool haveIdentifier, bool haveDeviceId,
|
public async Task SendAsync_UserIdSet_SendPayloadToUserAsync(bool haveIdentifier, bool haveDeviceId,
|
||||||
bool haveOrganizationId, SutProvider<PushController> sutProvider, Guid installationId, Guid userId,
|
bool haveOrganizationId, SutProvider<PushController> sutProvider, Guid installationId, Guid userId,
|
||||||
Guid identifier, Guid deviceId)
|
Guid identifier, Guid deviceId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
var expectedUserId = $"{installationId}_{userId}";
|
var expectedUserId = $"{installationId}_{userId}";
|
||||||
var expectedIdentifier = haveIdentifier ? $"{installationId}_{identifier}" : null;
|
var expectedIdentifier = haveIdentifier ? $"{installationId}_{identifier}" : null;
|
||||||
var expectedDeviceId = haveDeviceId ? $"{installationId}_{deviceId}" : null;
|
var expectedDeviceId = haveDeviceId ? $"{installationId}_{deviceId}" : null;
|
||||||
|
|
||||||
await sutProvider.Sut.PostSend(new PushSendRequestModel
|
await sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
{
|
{
|
||||||
Type = PushType.SyncNotificationCreate,
|
Type = PushType.SyncNotificationCreate,
|
||||||
UserId = userId.ToString(),
|
UserId = userId.ToString(),
|
||||||
OrganizationId = haveOrganizationId ? Guid.NewGuid().ToString() : null,
|
OrganizationId = haveOrganizationId ? Guid.NewGuid().ToString() : null,
|
||||||
|
InstallationId = null,
|
||||||
Payload = "test-payload",
|
Payload = "test-payload",
|
||||||
DeviceId = haveDeviceId ? deviceId.ToString() : null,
|
DeviceId = haveDeviceId ? deviceId.ToString() : null,
|
||||||
Identifier = haveIdentifier ? identifier.ToString() : null,
|
Identifier = haveIdentifier ? identifier.ToString() : null,
|
||||||
@@ -110,29 +112,30 @@ public class PushControllerTests
|
|||||||
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToInstallationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(true, true)]
|
[RepeatingPatternBitAutoData([false, true], [false, true])]
|
||||||
[BitAutoData(true, false)]
|
|
||||||
[BitAutoData(false, true)]
|
|
||||||
[BitAutoData(false, false)]
|
|
||||||
public async Task SendAsync_OrganizationIdSet_SendPayloadToOrganizationAsync(bool haveIdentifier, bool haveDeviceId,
|
public async Task SendAsync_OrganizationIdSet_SendPayloadToOrganizationAsync(bool haveIdentifier, bool haveDeviceId,
|
||||||
SutProvider<PushController> sutProvider, Guid installationId, Guid organizationId, Guid identifier,
|
SutProvider<PushController> sutProvider, Guid installationId, Guid organizationId, Guid identifier,
|
||||||
Guid deviceId)
|
Guid deviceId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
var expectedOrganizationId = $"{installationId}_{organizationId}";
|
var expectedOrganizationId = $"{installationId}_{organizationId}";
|
||||||
var expectedIdentifier = haveIdentifier ? $"{installationId}_{identifier}" : null;
|
var expectedIdentifier = haveIdentifier ? $"{installationId}_{identifier}" : null;
|
||||||
var expectedDeviceId = haveDeviceId ? $"{installationId}_{deviceId}" : null;
|
var expectedDeviceId = haveDeviceId ? $"{installationId}_{deviceId}" : null;
|
||||||
|
|
||||||
await sutProvider.Sut.PostSend(new PushSendRequestModel
|
await sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
{
|
{
|
||||||
Type = PushType.SyncNotificationCreate,
|
Type = PushType.SyncNotificationCreate,
|
||||||
UserId = null,
|
UserId = null,
|
||||||
OrganizationId = organizationId.ToString(),
|
OrganizationId = organizationId.ToString(),
|
||||||
|
InstallationId = null,
|
||||||
Payload = "test-payload",
|
Payload = "test-payload",
|
||||||
DeviceId = haveDeviceId ? deviceId.ToString() : null,
|
DeviceId = haveDeviceId ? deviceId.ToString() : null,
|
||||||
Identifier = haveIdentifier ? identifier.ToString() : null,
|
Identifier = haveIdentifier ? identifier.ToString() : null,
|
||||||
@@ -145,5 +148,141 @@ public class PushControllerTests
|
|||||||
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
.SendPayloadToUserAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(), Arg.Any<string>(),
|
.SendPayloadToUserAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(), Arg.Any<string>(),
|
||||||
Arg.Any<string?>(), Arg.Any<ClientType?>());
|
Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToInstallationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([false, true], [false, true])]
|
||||||
|
public async Task SendAsync_InstallationIdSet_SendPayloadToInstallationAsync(bool haveIdentifier, bool haveDeviceId,
|
||||||
|
SutProvider<PushController> sutProvider, Guid installationId, Guid identifier, Guid deviceId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
|
var expectedIdentifier = haveIdentifier ? $"{installationId}_{identifier}" : null;
|
||||||
|
var expectedDeviceId = haveDeviceId ? $"{installationId}_{deviceId}" : null;
|
||||||
|
|
||||||
|
await sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
|
{
|
||||||
|
Type = PushType.SyncNotificationCreate,
|
||||||
|
UserId = null,
|
||||||
|
OrganizationId = null,
|
||||||
|
InstallationId = installationId.ToString(),
|
||||||
|
Payload = "test-payload",
|
||||||
|
DeviceId = haveDeviceId ? deviceId.ToString() : null,
|
||||||
|
Identifier = haveIdentifier ? identifier.ToString() : null,
|
||||||
|
ClientType = ClientType.All,
|
||||||
|
});
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
|
||||||
|
.SendPayloadToInstallationAsync(installationId.ToString(), PushType.SyncNotificationCreate, "test-payload",
|
||||||
|
expectedIdentifier, expectedDeviceId, ClientType.All);
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToUserAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(), Arg.Any<string>(),
|
||||||
|
Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task SendAsync_InstallationIdNotMatching_BadRequest(SutProvider<PushController> sutProvider,
|
||||||
|
Guid installationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
|
sutProvider.Sut.SendAsync(new PushSendRequestModel
|
||||||
|
{
|
||||||
|
Type = PushType.SyncNotificationCreate,
|
||||||
|
UserId = null,
|
||||||
|
OrganizationId = null,
|
||||||
|
InstallationId = Guid.NewGuid().ToString(),
|
||||||
|
Payload = "test-payload",
|
||||||
|
DeviceId = null,
|
||||||
|
Identifier = null,
|
||||||
|
ClientType = ClientType.All,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Assert.Equal("InstallationId does not match current context.", exception.Message);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToInstallationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToOrganizationAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(),
|
||||||
|
Arg.Any<string>(), Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
await sutProvider.GetDependency<IPushNotificationService>().Received(0)
|
||||||
|
.SendPayloadToUserAsync(Arg.Any<string>(), Arg.Any<PushType>(), Arg.Any<object>(), Arg.Any<string>(),
|
||||||
|
Arg.Any<string?>(), Arg.Any<ClientType?>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(false, true)]
|
||||||
|
[BitAutoData(false, false)]
|
||||||
|
[BitAutoData(true, true)]
|
||||||
|
public async Task RegisterAsync_InstallationIdNotSetOrSelfHosted_BadRequest(bool haveInstallationId,
|
||||||
|
bool selfHosted,
|
||||||
|
SutProvider<PushController> sutProvider, Guid installationId, Guid userId, Guid identifier, Guid deviceId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = selfHosted;
|
||||||
|
if (haveInstallationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
|
sutProvider.Sut.RegisterAsync(new PushRegistrationRequestModel
|
||||||
|
{
|
||||||
|
DeviceId = deviceId.ToString(),
|
||||||
|
PushToken = "test-push-token",
|
||||||
|
UserId = userId.ToString(),
|
||||||
|
Type = DeviceType.Android,
|
||||||
|
Identifier = identifier.ToString()
|
||||||
|
}));
|
||||||
|
|
||||||
|
Assert.Equal("Not correctly configured for push relays.", exception.Message);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IPushRegistrationService>().Received(0)
|
||||||
|
.CreateOrUpdateRegistrationAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(), Arg.Any<string>(),
|
||||||
|
Arg.Any<DeviceType>(), Arg.Any<IEnumerable<string>>(), Arg.Any<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task? RegisterAsync_ValidModel_CreatedOrUpdatedRegistration(SutProvider<PushController> sutProvider,
|
||||||
|
Guid installationId, Guid userId, Guid identifier, Guid deviceId, Guid organizationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted = false;
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||||
|
|
||||||
|
var expectedUserId = $"{installationId}_{userId}";
|
||||||
|
var expectedIdentifier = $"{installationId}_{identifier}";
|
||||||
|
var expectedDeviceId = $"{installationId}_{deviceId}";
|
||||||
|
var expectedOrganizationId = $"{installationId}_{organizationId}";
|
||||||
|
|
||||||
|
await sutProvider.Sut.RegisterAsync(new PushRegistrationRequestModel
|
||||||
|
{
|
||||||
|
DeviceId = deviceId.ToString(),
|
||||||
|
PushToken = "test-push-token",
|
||||||
|
UserId = userId.ToString(),
|
||||||
|
Type = DeviceType.Android,
|
||||||
|
Identifier = identifier.ToString(),
|
||||||
|
OrganizationIds = [organizationId.ToString()],
|
||||||
|
InstallationId = installationId.ToString()
|
||||||
|
});
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IPushRegistrationService>().Received(1)
|
||||||
|
.CreateOrUpdateRegistrationAsync("test-push-token", expectedDeviceId, expectedUserId,
|
||||||
|
expectedIdentifier, DeviceType.Android, Arg.Do<IEnumerable<string>>(organizationIds =>
|
||||||
|
{
|
||||||
|
var organizationIdsList = organizationIds.ToList();
|
||||||
|
Assert.Contains(expectedOrganizationId, organizationIdsList);
|
||||||
|
Assert.Single(organizationIdsList);
|
||||||
|
}), installationId.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
|
||||||
|
public class RepeatingPatternBitAutoDataAttribute : BitAutoDataAttribute
|
||||||
|
{
|
||||||
|
private readonly List<object[]> _repeatingDataList;
|
||||||
|
|
||||||
|
public RepeatingPatternBitAutoDataAttribute(object[] first)
|
||||||
|
{
|
||||||
|
_repeatingDataList = AllValues([first]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepeatingPatternBitAutoDataAttribute(object[] first, object[] second)
|
||||||
|
{
|
||||||
|
_repeatingDataList = AllValues([first, second]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepeatingPatternBitAutoDataAttribute(object[] first, object[] second, object[] third)
|
||||||
|
{
|
||||||
|
_repeatingDataList = AllValues([first, second, third]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
|
||||||
|
{
|
||||||
|
foreach (var repeatingData in _repeatingDataList)
|
||||||
|
{
|
||||||
|
var bitData = base.GetData(testMethod).First();
|
||||||
|
for (var i = 0; i < repeatingData.Length; i++)
|
||||||
|
{
|
||||||
|
bitData[i] = repeatingData[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return bitData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<object[]> AllValues(object[][] parameterToPatternValues)
|
||||||
|
{
|
||||||
|
var result = new List<object[]>();
|
||||||
|
GenerateCombinations(parameterToPatternValues, new object[parameterToPatternValues.Length], 0, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GenerateCombinations(object[][] parameterToPatternValues, object[] current, int index,
|
||||||
|
List<object[]> result)
|
||||||
|
{
|
||||||
|
if (index == current.Length)
|
||||||
|
{
|
||||||
|
result.Add((object[])current.Clone());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var patternValues = parameterToPatternValues[index];
|
||||||
|
|
||||||
|
foreach (var value in patternValues)
|
||||||
|
{
|
||||||
|
current[index] = value;
|
||||||
|
GenerateCombinations(parameterToPatternValues, current, index + 1, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,19 +12,15 @@ namespace Bit.Core.Test.Models.Api.Request;
|
|||||||
public class PushSendRequestModelTests
|
public class PushSendRequestModelTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null, null)]
|
[RepeatingPatternBitAutoData([null, "", " "], [null, "", " "], [null, "", " "])]
|
||||||
[InlineData(null, "")]
|
public void Validate_UserIdOrganizationIdInstallationIdNullOrEmpty_Invalid(string? userId, string? organizationId,
|
||||||
[InlineData(null, " ")]
|
string? installationId)
|
||||||
[InlineData("", null)]
|
|
||||||
[InlineData(" ", null)]
|
|
||||||
[InlineData("", "")]
|
|
||||||
[InlineData(" ", " ")]
|
|
||||||
public void Validate_UserIdOrganizationIdNullOrEmpty_Invalid(string? userId, string? organizationId)
|
|
||||||
{
|
{
|
||||||
var model = new PushSendRequestModel
|
var model = new PushSendRequestModel
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
OrganizationId = organizationId,
|
OrganizationId = organizationId,
|
||||||
|
InstallationId = installationId,
|
||||||
Type = PushType.SyncCiphers,
|
Type = PushType.SyncCiphers,
|
||||||
Payload = "test"
|
Payload = "test"
|
||||||
};
|
};
|
||||||
@@ -32,7 +28,65 @@ public class PushSendRequestModelTests
|
|||||||
var results = Validate(model);
|
var results = Validate(model);
|
||||||
|
|
||||||
Assert.Single(results);
|
Assert.Single(results);
|
||||||
Assert.Contains(results, result => result.ErrorMessage == "UserId or OrganizationId is required.");
|
Assert.Contains(results,
|
||||||
|
result => result.ErrorMessage == "UserId or OrganizationId or InstallationId is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([null, "", " "], [null, "", " "])]
|
||||||
|
public void Validate_UserIdProvidedOrganizationIdInstallationIdNullOrEmpty_Valid(string? organizationId,
|
||||||
|
string? installationId)
|
||||||
|
{
|
||||||
|
var model = new PushSendRequestModel
|
||||||
|
{
|
||||||
|
UserId = Guid.NewGuid().ToString(),
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
InstallationId = installationId,
|
||||||
|
Type = PushType.SyncCiphers,
|
||||||
|
Payload = "test"
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Validate(model);
|
||||||
|
|
||||||
|
Assert.Empty(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([null, "", " "], [null, "", " "])]
|
||||||
|
public void Validate_OrganizationIdProvidedUserIdInstallationIdNullOrEmpty_Valid(string? userId,
|
||||||
|
string? installationId)
|
||||||
|
{
|
||||||
|
var model = new PushSendRequestModel
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
OrganizationId = Guid.NewGuid().ToString(),
|
||||||
|
InstallationId = installationId,
|
||||||
|
Type = PushType.SyncCiphers,
|
||||||
|
Payload = "test"
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Validate(model);
|
||||||
|
|
||||||
|
Assert.Empty(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([null, "", " "], [null, "", " "])]
|
||||||
|
public void Validate_InstallationIdProvidedUserIdOrganizationIdNullOrEmpty_Valid(string? userId,
|
||||||
|
string? organizationId)
|
||||||
|
{
|
||||||
|
var model = new PushSendRequestModel
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
InstallationId = Guid.NewGuid().ToString(),
|
||||||
|
Type = PushType.SyncCiphers,
|
||||||
|
Payload = "test"
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = Validate(model);
|
||||||
|
|
||||||
|
Assert.Empty(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.Models.Data;
|
|||||||
using Bit.Core.NotificationCenter.Entities;
|
using Bit.Core.NotificationCenter.Entities;
|
||||||
using Bit.Core.NotificationHub;
|
using Bit.Core.NotificationHub;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
@@ -21,9 +22,11 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
[NotificationCustomize]
|
[NotificationCustomize]
|
||||||
public async Task PushSyncNotificationCreateAsync_Global_NotSent(
|
public async Task PushSyncNotificationCreateAsync_GlobalInstallationIdDefault_NotSent(
|
||||||
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification)
|
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = default;
|
||||||
|
|
||||||
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
||||||
|
|
||||||
await sutProvider.GetDependency<INotificationHubPool>()
|
await sutProvider.GetDependency<INotificationHubPool>()
|
||||||
@@ -36,6 +39,53 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
[NotificationCustomize]
|
||||||
|
public async Task PushSyncNotificationCreateAsync_GlobalInstallationIdSetClientTypeAll_SentToInstallationId(
|
||||||
|
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification, Guid installationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
notification.ClientType = ClientType.All;
|
||||||
|
var expectedSyncNotification = ToSyncNotificationPushNotification(notification, null);
|
||||||
|
expectedSyncNotification.InstallationId = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate,
|
||||||
|
expectedSyncNotification,
|
||||||
|
$"(template:payload && installationId:{installationId})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(ClientType.Browser)]
|
||||||
|
[BitAutoData(ClientType.Desktop)]
|
||||||
|
[BitAutoData(ClientType.Web)]
|
||||||
|
[BitAutoData(ClientType.Mobile)]
|
||||||
|
[NotificationCustomize]
|
||||||
|
public async Task
|
||||||
|
PushSyncNotificationCreateAsync_GlobalInstallationIdSetClientTypeNotAll_SentToInstallationIdAndClientType(
|
||||||
|
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider,
|
||||||
|
Notification notification, Guid installationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
notification.ClientType = clientType;
|
||||||
|
var expectedSyncNotification = ToSyncNotificationPushNotification(notification, null);
|
||||||
|
expectedSyncNotification.InstallationId = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationCreate,
|
||||||
|
expectedSyncNotification,
|
||||||
|
$"(template:payload && installationId:{installationId} && clientType:{clientType})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false)]
|
[BitAutoData(false)]
|
||||||
[BitAutoData(true)]
|
[BitAutoData(true)]
|
||||||
@@ -158,10 +208,12 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
[BitAutoData(false)]
|
[BitAutoData(false)]
|
||||||
[BitAutoData(true)]
|
[BitAutoData(true)]
|
||||||
[NotificationCustomize]
|
[NotificationCustomize]
|
||||||
public async Task PushSyncNotificationUpdateAsync_Global_NotSent(bool notificationStatusNull,
|
public async Task PushSyncNotificationUpdateAsync_GlobalInstallationIdDefault_NotSent(bool notificationStatusNull,
|
||||||
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification,
|
SutProvider<NotificationHubPushNotificationService> sutProvider, Notification notification,
|
||||||
NotificationStatus notificationStatus)
|
NotificationStatus notificationStatus)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = default;
|
||||||
|
|
||||||
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification,
|
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification,
|
||||||
notificationStatusNull ? null : notificationStatus);
|
notificationStatusNull ? null : notificationStatus);
|
||||||
|
|
||||||
@@ -176,10 +228,59 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, false)]
|
[BitAutoData(false)]
|
||||||
[BitAutoData(false, true)]
|
[BitAutoData(true)]
|
||||||
[BitAutoData(true, false)]
|
[NotificationCustomize]
|
||||||
[BitAutoData(true, true)]
|
public async Task PushSyncNotificationUpdateAsync_GlobalInstallationIdSetClientTypeAll_SentToInstallationId(
|
||||||
|
bool notificationStatusNull, SutProvider<NotificationHubPushNotificationService> sutProvider,
|
||||||
|
Notification notification, NotificationStatus notificationStatus, Guid installationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
notification.ClientType = ClientType.All;
|
||||||
|
|
||||||
|
var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus;
|
||||||
|
var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus);
|
||||||
|
expectedSyncNotification.InstallationId = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate,
|
||||||
|
expectedSyncNotification,
|
||||||
|
$"(template:payload && installationId:{installationId})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([false, true],
|
||||||
|
[ClientType.Browser, ClientType.Desktop, ClientType.Web, ClientType.Mobile])]
|
||||||
|
[NotificationCustomize]
|
||||||
|
public async Task
|
||||||
|
PushSyncNotificationUpdateAsync_GlobalInstallationIdSetClientTypeNotAll_SentToInstallationIdAndClientType(
|
||||||
|
bool notificationStatusNull, ClientType clientType,
|
||||||
|
SutProvider<NotificationHubPushNotificationService> sutProvider,
|
||||||
|
Notification notification, NotificationStatus notificationStatus, Guid installationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
notification.ClientType = clientType;
|
||||||
|
|
||||||
|
var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus;
|
||||||
|
var expectedSyncNotification = ToSyncNotificationPushNotification(notification, expectedNotificationStatus);
|
||||||
|
expectedSyncNotification.InstallationId = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, PushType.SyncNotificationUpdate,
|
||||||
|
expectedSyncNotification,
|
||||||
|
$"(template:payload && installationId:{installationId} && clientType:{clientType})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[RepeatingPatternBitAutoData([false, true], [false, true])]
|
||||||
[NotificationCustomize(false)]
|
[NotificationCustomize(false)]
|
||||||
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedClientTypeAll_SentToUser(
|
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedClientTypeAll_SentToUser(
|
||||||
bool organizationIdNull, bool notificationStatusNull,
|
bool organizationIdNull, bool notificationStatusNull,
|
||||||
@@ -206,14 +307,8 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, ClientType.Browser)]
|
[RepeatingPatternBitAutoData([false, true],
|
||||||
[BitAutoData(false, ClientType.Desktop)]
|
[ClientType.Browser, ClientType.Desktop, ClientType.Web, ClientType.Mobile])]
|
||||||
[BitAutoData(false, ClientType.Web)]
|
|
||||||
[BitAutoData(false, ClientType.Mobile)]
|
|
||||||
[BitAutoData(true, ClientType.Browser)]
|
|
||||||
[BitAutoData(true, ClientType.Desktop)]
|
|
||||||
[BitAutoData(true, ClientType.Web)]
|
|
||||||
[BitAutoData(true, ClientType.Mobile)]
|
|
||||||
[NotificationCustomize(false)]
|
[NotificationCustomize(false)]
|
||||||
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser(
|
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdNullClientTypeNotAll_SentToUser(
|
||||||
bool notificationStatusNull, ClientType clientType,
|
bool notificationStatusNull, ClientType clientType,
|
||||||
@@ -236,14 +331,8 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, ClientType.Browser)]
|
[RepeatingPatternBitAutoData([false, true],
|
||||||
[BitAutoData(false, ClientType.Desktop)]
|
[ClientType.Browser, ClientType.Desktop, ClientType.Web, ClientType.Mobile])]
|
||||||
[BitAutoData(false, ClientType.Web)]
|
|
||||||
[BitAutoData(false, ClientType.Mobile)]
|
|
||||||
[BitAutoData(true, ClientType.Browser)]
|
|
||||||
[BitAutoData(true, ClientType.Desktop)]
|
|
||||||
[BitAutoData(true, ClientType.Web)]
|
|
||||||
[BitAutoData(true, ClientType.Mobile)]
|
|
||||||
[NotificationCustomize(false)]
|
[NotificationCustomize(false)]
|
||||||
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser(
|
public async Task PushSyncNotificationUpdateAsync_UserIdProvidedOrganizationIdProvidedClientTypeNotAll_SentToUser(
|
||||||
bool notificationStatusNull, ClientType clientType,
|
bool notificationStatusNull, ClientType clientType,
|
||||||
@@ -288,14 +377,8 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, ClientType.Browser)]
|
[RepeatingPatternBitAutoData([false, true],
|
||||||
[BitAutoData(false, ClientType.Desktop)]
|
[ClientType.Browser, ClientType.Desktop, ClientType.Web, ClientType.Mobile])]
|
||||||
[BitAutoData(false, ClientType.Web)]
|
|
||||||
[BitAutoData(false, ClientType.Mobile)]
|
|
||||||
[BitAutoData(true, ClientType.Browser)]
|
|
||||||
[BitAutoData(true, ClientType.Desktop)]
|
|
||||||
[BitAutoData(true, ClientType.Web)]
|
|
||||||
[BitAutoData(true, ClientType.Mobile)]
|
|
||||||
[NotificationCustomize(false)]
|
[NotificationCustomize(false)]
|
||||||
public async Task
|
public async Task
|
||||||
PushSyncNotificationUpdateAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization(
|
PushSyncNotificationUpdateAsync_UserIdNullOrganizationIdProvidedClientTypeNotAll_SentToOrganization(
|
||||||
@@ -390,6 +473,42 @@ public class NotificationHubPushNotificationServiceTests
|
|||||||
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData([null])]
|
||||||
|
[BitAutoData(ClientType.All)]
|
||||||
|
public async Task SendPayloadToInstallationAsync_ClientTypeNullOrAll_SentToInstallation(ClientType? clientType,
|
||||||
|
SutProvider<NotificationHubPushNotificationService> sutProvider, Guid installationId, PushType pushType,
|
||||||
|
string payload, string identifier)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.SendPayloadToInstallationAsync(installationId.ToString(), pushType, payload, identifier,
|
||||||
|
null, clientType);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
|
||||||
|
$"(template:payload && installationId:{installationId} && !deviceIdentifier:{identifier})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(ClientType.Browser)]
|
||||||
|
[BitAutoData(ClientType.Desktop)]
|
||||||
|
[BitAutoData(ClientType.Mobile)]
|
||||||
|
[BitAutoData(ClientType.Web)]
|
||||||
|
public async Task SendPayloadToInstallationAsync_ClientTypeExplicit_SentToInstallationAndClientType(
|
||||||
|
ClientType clientType, SutProvider<NotificationHubPushNotificationService> sutProvider, Guid installationId,
|
||||||
|
PushType pushType, string payload, string identifier)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.SendPayloadToInstallationAsync(installationId.ToString(), pushType, payload, identifier,
|
||||||
|
null, clientType);
|
||||||
|
|
||||||
|
await AssertSendTemplateNotificationAsync(sutProvider, pushType, payload,
|
||||||
|
$"(template:payload && installationId:{installationId} && !deviceIdentifier:{identifier} && clientType:{clientType})");
|
||||||
|
await sutProvider.GetDependency<IInstallationDeviceRepository>()
|
||||||
|
.Received(0)
|
||||||
|
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||||
|
}
|
||||||
|
|
||||||
private static SyncNotificationPushNotification ToSyncNotificationPushNotification(Notification notification,
|
private static SyncNotificationPushNotification ToSyncNotificationPushNotification(Notification notification,
|
||||||
NotificationStatus? notificationStatus) =>
|
NotificationStatus? notificationStatus) =>
|
||||||
new()
|
new()
|
||||||
|
|||||||
@@ -14,15 +14,13 @@ namespace Bit.Core.Test.NotificationHub;
|
|||||||
public class NotificationHubPushRegistrationServiceTests
|
public class NotificationHubPushRegistrationServiceTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData([null])]
|
[RepeatingPatternBitAutoData([null, "", " "])]
|
||||||
[BitAutoData("")]
|
|
||||||
[BitAutoData(" ")]
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync_PushTokenNullOrEmpty_InstallationNotCreated(string? pushToken,
|
public async Task CreateOrUpdateRegistrationAsync_PushTokenNullOrEmpty_InstallationNotCreated(string? pushToken,
|
||||||
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid identifier,
|
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid identifier,
|
||||||
Guid organizationId)
|
Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
||||||
identifier.ToString(), DeviceType.Android, [organizationId.ToString()]);
|
identifier.ToString(), DeviceType.Android, [organizationId.ToString()], installationId.ToString());
|
||||||
|
|
||||||
sutProvider.GetDependency<INotificationHubPool>()
|
sutProvider.GetDependency<INotificationHubPool>()
|
||||||
.Received(0)
|
.Received(0)
|
||||||
@@ -30,13 +28,11 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, false)]
|
[RepeatingPatternBitAutoData([false, true], [false, true], [false, true])]
|
||||||
[BitAutoData(false, true)]
|
|
||||||
[BitAutoData(true, false)]
|
|
||||||
[BitAutoData(true, true)]
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroid_InstallationCreated(bool identifierNull,
|
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroid_InstallationCreated(bool identifierNull,
|
||||||
bool partOfOrganizationId, SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId,
|
bool partOfOrganizationId, bool installationIdNull,
|
||||||
Guid userId, Guid? identifier, Guid organizationId)
|
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid? identifier,
|
||||||
|
Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
||||||
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
||||||
@@ -45,7 +41,8 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
||||||
identifierNull ? null : identifier.ToString(), DeviceType.Android,
|
identifierNull ? null : identifier.ToString(), DeviceType.Android,
|
||||||
partOfOrganizationId ? [organizationId.ToString()] : []);
|
partOfOrganizationId ? [organizationId.ToString()] : [],
|
||||||
|
installationIdNull ? null : installationId.ToString());
|
||||||
|
|
||||||
sutProvider.GetDependency<INotificationHubPool>()
|
sutProvider.GetDependency<INotificationHubPool>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -60,6 +57,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
installation.Tags.Contains("clientType:Mobile") &&
|
installation.Tags.Contains("clientType:Mobile") &&
|
||||||
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
||||||
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
||||||
|
(installationIdNull || installation.Tags.Contains($"installationId:{installationId}")) &&
|
||||||
installation.Templates.Count == 3));
|
installation.Templates.Count == 3));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -73,6 +71,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -86,6 +85,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -99,17 +99,16 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, false)]
|
[RepeatingPatternBitAutoData([false, true], [false, true], [false, true])]
|
||||||
[BitAutoData(false, true)]
|
|
||||||
[BitAutoData(true, false)]
|
|
||||||
[BitAutoData(true, true)]
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeIOS_InstallationCreated(bool identifierNull,
|
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeIOS_InstallationCreated(bool identifierNull,
|
||||||
bool partOfOrganizationId, SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId,
|
bool partOfOrganizationId, bool installationIdNull,
|
||||||
Guid userId, Guid identifier, Guid organizationId)
|
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid identifier,
|
||||||
|
Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
||||||
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
||||||
@@ -118,7 +117,8 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
||||||
identifierNull ? null : identifier.ToString(), DeviceType.iOS,
|
identifierNull ? null : identifier.ToString(), DeviceType.iOS,
|
||||||
partOfOrganizationId ? [organizationId.ToString()] : []);
|
partOfOrganizationId ? [organizationId.ToString()] : [],
|
||||||
|
installationIdNull ? null : installationId.ToString());
|
||||||
|
|
||||||
sutProvider.GetDependency<INotificationHubPool>()
|
sutProvider.GetDependency<INotificationHubPool>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -133,6 +133,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
installation.Tags.Contains("clientType:Mobile") &&
|
installation.Tags.Contains("clientType:Mobile") &&
|
||||||
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
||||||
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
||||||
|
(installationIdNull || installation.Tags.Contains($"installationId:{installationId}")) &&
|
||||||
installation.Templates.Count == 3));
|
installation.Templates.Count == 3));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -146,6 +147,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -159,6 +161,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -172,17 +175,16 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(false, false)]
|
[RepeatingPatternBitAutoData([false, true], [false, true], [false, true])]
|
||||||
[BitAutoData(false, true)]
|
|
||||||
[BitAutoData(true, false)]
|
|
||||||
[BitAutoData(true, true)]
|
|
||||||
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroidAmazon_InstallationCreated(bool identifierNull,
|
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroidAmazon_InstallationCreated(bool identifierNull,
|
||||||
bool partOfOrganizationId, SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId,
|
bool partOfOrganizationId, bool installationIdNull,
|
||||||
Guid userId, Guid identifier, Guid organizationId)
|
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId,
|
||||||
|
Guid userId, Guid identifier, Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
||||||
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
||||||
@@ -191,7 +193,8 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
|
|
||||||
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
||||||
identifierNull ? null : identifier.ToString(), DeviceType.AndroidAmazon,
|
identifierNull ? null : identifier.ToString(), DeviceType.AndroidAmazon,
|
||||||
partOfOrganizationId ? [organizationId.ToString()] : []);
|
partOfOrganizationId ? [organizationId.ToString()] : [],
|
||||||
|
installationIdNull ? null : installationId.ToString());
|
||||||
|
|
||||||
sutProvider.GetDependency<INotificationHubPool>()
|
sutProvider.GetDependency<INotificationHubPool>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -206,6 +209,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
installation.Tags.Contains("clientType:Mobile") &&
|
installation.Tags.Contains("clientType:Mobile") &&
|
||||||
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
(identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) &&
|
||||||
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
(!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) &&
|
||||||
|
(installationIdNull || installation.Tags.Contains($"installationId:{installationId}")) &&
|
||||||
installation.Templates.Count == 3));
|
installation.Templates.Count == 3));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -219,6 +223,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -232,6 +237,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:message_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
await notificationHubClient
|
await notificationHubClient
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -245,6 +251,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
"clientType:Mobile",
|
"clientType:Mobile",
|
||||||
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}",
|
||||||
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
partOfOrganizationId ? $"organizationId:{organizationId}" : null,
|
||||||
|
installationIdNull ? null : $"installationId:{installationId}",
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,7 +261,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
[BitAutoData(DeviceType.MacOsDesktop)]
|
[BitAutoData(DeviceType.MacOsDesktop)]
|
||||||
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeNotMobile_InstallationCreated(DeviceType deviceType,
|
public async Task CreateOrUpdateRegistrationAsync_DeviceTypeNotMobile_InstallationCreated(DeviceType deviceType,
|
||||||
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid identifier,
|
SutProvider<NotificationHubPushRegistrationService> sutProvider, Guid deviceId, Guid userId, Guid identifier,
|
||||||
Guid organizationId)
|
Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
var notificationHubClient = Substitute.For<INotificationHubClient>();
|
||||||
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
sutProvider.GetDependency<INotificationHubPool>().ClientFor(Arg.Any<Guid>()).Returns(notificationHubClient);
|
||||||
@@ -262,7 +269,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
var pushToken = "test push token";
|
var pushToken = "test push token";
|
||||||
|
|
||||||
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(),
|
||||||
identifier.ToString(), deviceType, [organizationId.ToString()]);
|
identifier.ToString(), deviceType, [organizationId.ToString()], installationId.ToString());
|
||||||
|
|
||||||
sutProvider.GetDependency<INotificationHubPool>()
|
sutProvider.GetDependency<INotificationHubPool>()
|
||||||
.Received(1)
|
.Received(1)
|
||||||
@@ -276,6 +283,7 @@ public class NotificationHubPushRegistrationServiceTests
|
|||||||
installation.Tags.Contains($"clientType:{DeviceTypes.ToClientType(deviceType)}") &&
|
installation.Tags.Contains($"clientType:{DeviceTypes.ToClientType(deviceType)}") &&
|
||||||
installation.Tags.Contains($"deviceIdentifier:{identifier}") &&
|
installation.Tags.Contains($"deviceIdentifier:{identifier}") &&
|
||||||
installation.Tags.Contains($"organizationId:{organizationId}") &&
|
installation.Tags.Contains($"organizationId:{organizationId}") &&
|
||||||
|
installation.Tags.Contains($"installationId:{installationId}") &&
|
||||||
installation.Templates.Count == 0));
|
installation.Templates.Count == 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Models;
|
using Bit.Core.Models;
|
||||||
using Bit.Core.NotificationCenter.Entities;
|
using Bit.Core.NotificationCenter.Entities;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Test.AutoFixture;
|
using Bit.Core.Test.AutoFixture;
|
||||||
using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
|
using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
|
||||||
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||||
@@ -25,19 +26,43 @@ public class AzureQueuePushNotificationServiceTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
[NotificationCustomize]
|
[NotificationCustomize]
|
||||||
[CurrentContextCustomize]
|
[CurrentContextCustomize]
|
||||||
public async Task PushSyncNotificationCreateAsync_Notification_Sent(
|
public async Task PushSyncNotificationCreateAsync_NotificationGlobal_Sent(
|
||||||
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext, Guid installationId)
|
||||||
{
|
{
|
||||||
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
||||||
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
||||||
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
|
||||||
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
||||||
|
|
||||||
await sutProvider.GetDependency<QueueClient>().Received(1)
|
await sutProvider.GetDependency<QueueClient>().Received(1)
|
||||||
.SendMessageAsync(Arg.Is<string>(message =>
|
.SendMessageAsync(Arg.Is<string>(message =>
|
||||||
MatchMessage(PushType.SyncNotificationCreate, message, new SyncNotificationEquals(notification, null),
|
MatchMessage(PushType.SyncNotificationCreate, message,
|
||||||
|
new SyncNotificationEquals(notification, null, installationId),
|
||||||
|
deviceIdentifier.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
[NotificationCustomize(false)]
|
||||||
|
[CurrentContextCustomize]
|
||||||
|
public async Task PushSyncNotificationCreateAsync_NotificationNotGlobal_Sent(
|
||||||
|
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||||
|
ICurrentContext currentContext, Guid installationId)
|
||||||
|
{
|
||||||
|
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
||||||
|
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
||||||
|
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationCreateAsync(notification);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<QueueClient>().Received(1)
|
||||||
|
.SendMessageAsync(Arg.Is<string>(message =>
|
||||||
|
MatchMessage(PushType.SyncNotificationCreate, message,
|
||||||
|
new SyncNotificationEquals(notification, null, null),
|
||||||
deviceIdentifier.ToString())));
|
deviceIdentifier.ToString())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,21 +72,47 @@ public class AzureQueuePushNotificationServiceTests
|
|||||||
[NotificationCustomize]
|
[NotificationCustomize]
|
||||||
[NotificationStatusCustomize]
|
[NotificationStatusCustomize]
|
||||||
[CurrentContextCustomize]
|
[CurrentContextCustomize]
|
||||||
public async Task PushSyncNotificationUpdateAsync_Notification_Sent(bool notificationStatusNull,
|
public async Task PushSyncNotificationUpdateAsync_NotificationGlobal_Sent(bool notificationStatusNull,
|
||||||
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||||
ICurrentContext currentContext, NotificationStatus notificationStatus)
|
ICurrentContext currentContext, NotificationStatus notificationStatus, Guid installationId)
|
||||||
{
|
{
|
||||||
var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus;
|
var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus;
|
||||||
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
||||||
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
||||||
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
|
||||||
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus);
|
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus);
|
||||||
|
|
||||||
await sutProvider.GetDependency<QueueClient>().Received(1)
|
await sutProvider.GetDependency<QueueClient>().Received(1)
|
||||||
.SendMessageAsync(Arg.Is<string>(message =>
|
.SendMessageAsync(Arg.Is<string>(message =>
|
||||||
MatchMessage(PushType.SyncNotificationUpdate, message,
|
MatchMessage(PushType.SyncNotificationUpdate, message,
|
||||||
new SyncNotificationEquals(notification, expectedNotificationStatus),
|
new SyncNotificationEquals(notification, expectedNotificationStatus, installationId),
|
||||||
|
deviceIdentifier.ToString())));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(false)]
|
||||||
|
[BitAutoData(true)]
|
||||||
|
[NotificationCustomize(false)]
|
||||||
|
[NotificationStatusCustomize]
|
||||||
|
[CurrentContextCustomize]
|
||||||
|
public async Task PushSyncNotificationUpdateAsync_NotificationNotGlobal_Sent(bool notificationStatusNull,
|
||||||
|
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||||
|
ICurrentContext currentContext, NotificationStatus notificationStatus, Guid installationId)
|
||||||
|
{
|
||||||
|
var expectedNotificationStatus = notificationStatusNull ? null : notificationStatus;
|
||||||
|
currentContext.DeviceIdentifier.Returns(deviceIdentifier.ToString());
|
||||||
|
sutProvider.GetDependency<IHttpContextAccessor>().HttpContext!.RequestServices
|
||||||
|
.GetService(Arg.Any<Type>()).Returns(currentContext);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().Installation.Id = installationId;
|
||||||
|
|
||||||
|
await sutProvider.Sut.PushSyncNotificationUpdateAsync(notification, expectedNotificationStatus);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<QueueClient>().Received(1)
|
||||||
|
.SendMessageAsync(Arg.Is<string>(message =>
|
||||||
|
MatchMessage(PushType.SyncNotificationUpdate, message,
|
||||||
|
new SyncNotificationEquals(notification, expectedNotificationStatus, null),
|
||||||
deviceIdentifier.ToString())));
|
deviceIdentifier.ToString())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +127,10 @@ public class AzureQueuePushNotificationServiceTests
|
|||||||
pushNotificationData.ContextId == contextId;
|
pushNotificationData.ContextId == contextId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SyncNotificationEquals(Notification notification, NotificationStatus? notificationStatus)
|
private class SyncNotificationEquals(
|
||||||
|
Notification notification,
|
||||||
|
NotificationStatus? notificationStatus,
|
||||||
|
Guid? installationId)
|
||||||
: IEquatable<SyncNotificationPushNotification>
|
: IEquatable<SyncNotificationPushNotification>
|
||||||
{
|
{
|
||||||
public bool Equals(SyncNotificationPushNotification? other)
|
public bool Equals(SyncNotificationPushNotification? other)
|
||||||
@@ -85,6 +139,7 @@ public class AzureQueuePushNotificationServiceTests
|
|||||||
other.Id == notification.Id &&
|
other.Id == notification.Id &&
|
||||||
other.UserId == notification.UserId &&
|
other.UserId == notification.UserId &&
|
||||||
other.OrganizationId == notification.OrganizationId &&
|
other.OrganizationId == notification.OrganizationId &&
|
||||||
|
other.InstallationId == installationId &&
|
||||||
other.ClientType == notification.ClientType &&
|
other.ClientType == notification.ClientType &&
|
||||||
other.RevisionDate == notification.RevisionDate &&
|
other.RevisionDate == notification.RevisionDate &&
|
||||||
other.ReadDate == notificationStatus?.ReadDate &&
|
other.ReadDate == notificationStatus?.ReadDate &&
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@@ -19,7 +20,7 @@ public class DeviceServiceTests
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task SaveAsync_IdProvided_UpdatedRevisionDateAndPushRegistration(Guid id, Guid userId,
|
public async Task SaveAsync_IdProvided_UpdatedRevisionDateAndPushRegistration(Guid id, Guid userId,
|
||||||
Guid organizationId1, Guid organizationId2,
|
Guid organizationId1, Guid organizationId2, Guid installationId,
|
||||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
||||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
||||||
{
|
{
|
||||||
@@ -31,7 +32,9 @@ public class DeviceServiceTests
|
|||||||
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||||
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
||||||
.Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]);
|
.Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]);
|
||||||
var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository);
|
var globalSettings = Substitute.For<IGlobalSettings>();
|
||||||
|
globalSettings.Installation.Id.Returns(installationId);
|
||||||
|
var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository, globalSettings);
|
||||||
|
|
||||||
var device = new Device
|
var device = new Device
|
||||||
{
|
{
|
||||||
@@ -53,13 +56,13 @@ public class DeviceServiceTests
|
|||||||
Assert.Equal(2, organizationIdsList.Count);
|
Assert.Equal(2, organizationIdsList.Count);
|
||||||
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
||||||
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
||||||
}));
|
}), installationId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task SaveAsync_IdNotProvided_CreatedAndPushRegistration(Guid userId, Guid organizationId1,
|
public async Task SaveAsync_IdNotProvided_CreatedAndPushRegistration(Guid userId, Guid organizationId1,
|
||||||
Guid organizationId2,
|
Guid organizationId2, Guid installationId,
|
||||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
||||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
||||||
{
|
{
|
||||||
@@ -71,7 +74,9 @@ public class DeviceServiceTests
|
|||||||
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||||
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
||||||
.Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]);
|
.Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]);
|
||||||
var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository);
|
var globalSettings = Substitute.For<IGlobalSettings>();
|
||||||
|
globalSettings.Installation.Id.Returns(installationId);
|
||||||
|
var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository, globalSettings);
|
||||||
|
|
||||||
var device = new Device
|
var device = new Device
|
||||||
{
|
{
|
||||||
@@ -91,7 +96,7 @@ public class DeviceServiceTests
|
|||||||
Assert.Equal(2, organizationIdsList.Count);
|
Assert.Equal(2, organizationIdsList.Count);
|
||||||
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
||||||
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
||||||
}));
|
}), installationId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -79,4 +79,22 @@ public class MultiServicePushNotificationServiceTests
|
|||||||
.Received(1)
|
.Received(1)
|
||||||
.SendPayloadToOrganizationAsync(organizationId, type, payload, identifier, deviceId, clientType);
|
.SendPayloadToOrganizationAsync(organizationId, type, payload, identifier, deviceId, clientType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData([null, null])]
|
||||||
|
[BitAutoData(ClientType.All, null)]
|
||||||
|
[BitAutoData([null, "test device id"])]
|
||||||
|
[BitAutoData(ClientType.All, "test device id")]
|
||||||
|
public async Task SendPayloadToInstallationAsync_Message_Sent(ClientType? clientType, string? deviceId,
|
||||||
|
string installationId, PushType type, object payload, string identifier,
|
||||||
|
SutProvider<MultiServicePushNotificationService> sutProvider)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.SendPayloadToInstallationAsync(installationId, type, payload, identifier, deviceId,
|
||||||
|
clientType);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IEnumerable<IPushNotificationService>>()
|
||||||
|
.First()
|
||||||
|
.Received(1)
|
||||||
|
.SendPayloadToInstallationAsync(installationId, type, payload, identifier, deviceId, clientType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user