mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 08:32:50 -05: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:
@ -6,6 +6,7 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Test.AutoFixture;
|
||||
using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
|
||||
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||
@ -25,19 +26,43 @@ public class AzureQueuePushNotificationServiceTests
|
||||
[BitAutoData]
|
||||
[NotificationCustomize]
|
||||
[CurrentContextCustomize]
|
||||
public async Task PushSyncNotificationCreateAsync_Notification_Sent(
|
||||
public async Task PushSyncNotificationCreateAsync_NotificationGlobal_Sent(
|
||||
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||
ICurrentContext currentContext)
|
||||
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),
|
||||
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())));
|
||||
}
|
||||
|
||||
@ -47,21 +72,47 @@ public class AzureQueuePushNotificationServiceTests
|
||||
[NotificationCustomize]
|
||||
[NotificationStatusCustomize]
|
||||
[CurrentContextCustomize]
|
||||
public async Task PushSyncNotificationUpdateAsync_Notification_Sent(bool notificationStatusNull,
|
||||
public async Task PushSyncNotificationUpdateAsync_NotificationGlobal_Sent(bool notificationStatusNull,
|
||||
SutProvider<AzureQueuePushNotificationService> sutProvider, Notification notification, Guid deviceIdentifier,
|
||||
ICurrentContext currentContext, NotificationStatus notificationStatus)
|
||||
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),
|
||||
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())));
|
||||
}
|
||||
|
||||
@ -76,7 +127,10 @@ public class AzureQueuePushNotificationServiceTests
|
||||
pushNotificationData.ContextId == contextId;
|
||||
}
|
||||
|
||||
private class SyncNotificationEquals(Notification notification, NotificationStatus? notificationStatus)
|
||||
private class SyncNotificationEquals(
|
||||
Notification notification,
|
||||
NotificationStatus? notificationStatus,
|
||||
Guid? installationId)
|
||||
: IEquatable<SyncNotificationPushNotification>
|
||||
{
|
||||
public bool Equals(SyncNotificationPushNotification? other)
|
||||
@ -85,6 +139,7 @@ public class AzureQueuePushNotificationServiceTests
|
||||
other.Id == notification.Id &&
|
||||
other.UserId == notification.UserId &&
|
||||
other.OrganizationId == notification.OrganizationId &&
|
||||
other.InstallationId == installationId &&
|
||||
other.ClientType == notification.ClientType &&
|
||||
other.RevisionDate == notification.RevisionDate &&
|
||||
other.ReadDate == notificationStatus?.ReadDate &&
|
||||
|
@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
@ -19,7 +20,7 @@ public class DeviceServiceTests
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SaveAsync_IdProvided_UpdatedRevisionDateAndPushRegistration(Guid id, Guid userId,
|
||||
Guid organizationId1, Guid organizationId2,
|
||||
Guid organizationId1, Guid organizationId2, Guid installationId,
|
||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
||||
{
|
||||
@ -31,7 +32,9 @@ public class DeviceServiceTests
|
||||
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
||||
.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
|
||||
{
|
||||
@ -53,13 +56,13 @@ public class DeviceServiceTests
|
||||
Assert.Equal(2, organizationIdsList.Count);
|
||||
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
||||
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
||||
}));
|
||||
}), installationId.ToString());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SaveAsync_IdNotProvided_CreatedAndPushRegistration(Guid userId, Guid organizationId1,
|
||||
Guid organizationId2,
|
||||
Guid organizationId2, Guid installationId,
|
||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails1,
|
||||
OrganizationUserOrganizationDetails organizationUserOrganizationDetails2)
|
||||
{
|
||||
@ -71,7 +74,9 @@ public class DeviceServiceTests
|
||||
var organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType?>())
|
||||
.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
|
||||
{
|
||||
@ -91,7 +96,7 @@ public class DeviceServiceTests
|
||||
Assert.Equal(2, organizationIdsList.Count);
|
||||
Assert.Contains(organizationId1.ToString(), organizationIdsList);
|
||||
Assert.Contains(organizationId2.ToString(), organizationIdsList);
|
||||
}));
|
||||
}), installationId.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -79,4 +79,22 @@ public class MultiServicePushNotificationServiceTests
|
||||
.Received(1)
|
||||
.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