1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

[PM-24011] Create new policy sync push notification (#6594)

* create new policy sync push notification

* CR feedback

* add tests, fix typo
This commit is contained in:
Brandon Treston
2025-12-01 10:21:44 -05:00
committed by GitHub
parent 62cbe36ce1
commit a5ea603817
7 changed files with 210 additions and 6 deletions

View File

@@ -6,8 +6,11 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Platform.Push;
using Bit.Core.Services;
using Bit.Core.Test.AdminConsole.AutoFixture;
using Bit.Test.Common.AutoFixture;
@@ -95,7 +98,8 @@ public class SavePolicyCommandTests
Substitute.For<IPolicyRepository>(),
[new FakeSingleOrgPolicyValidator(), new FakeSingleOrgPolicyValidator()],
Substitute.For<TimeProvider>(),
Substitute.For<IPostSavePolicySideEffect>()));
Substitute.For<IPostSavePolicySideEffect>(),
Substitute.For<IPushNotificationService>()));
Assert.Contains("Duplicate PolicyValidator for SingleOrg policy", exception.Message);
}
@@ -360,6 +364,103 @@ public class SavePolicyCommandTests
.ExecuteSideEffectsAsync(default!, default!, default!);
}
[Theory, BitAutoData]
public async Task VNextSaveAsync_SendsPushNotification(
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
[Policy(PolicyType.SingleOrg, false)] Policy currentPolicy)
{
// Arrange
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
var sutProvider = SutProviderFactory([fakePolicyValidator]);
var savePolicyModel = new SavePolicyModel(policyUpdate);
currentPolicy.OrganizationId = policyUpdate.OrganizationId;
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policyUpdate.OrganizationId, policyUpdate.Type)
.Returns(currentPolicy);
ArrangeOrganization(sutProvider, policyUpdate);
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(policyUpdate.OrganizationId)
.Returns([currentPolicy]);
// Act
var result = await sutProvider.Sut.VNextSaveAsync(savePolicyModel);
// Assert
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
p.Type == PushType.PolicyChanged &&
p.Target == NotificationTarget.Organization &&
p.TargetId == policyUpdate.OrganizationId &&
p.ExcludeCurrentContext == false &&
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
p.Payload.Policy.Id == result.Id &&
p.Payload.Policy.Type == policyUpdate.Type &&
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
p.Payload.Policy.Data == policyUpdate.Data));
}
[Theory, BitAutoData]
public async Task SaveAsync_SendsPushNotification([PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate)
{
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
var sutProvider = SutProviderFactory([fakePolicyValidator]);
ArrangeOrganization(sutProvider, policyUpdate);
sutProvider.GetDependency<IPolicyRepository>().GetManyByOrganizationIdAsync(policyUpdate.OrganizationId).Returns([]);
var result = await sutProvider.Sut.SaveAsync(policyUpdate);
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
p.Type == PushType.PolicyChanged &&
p.Target == NotificationTarget.Organization &&
p.TargetId == policyUpdate.OrganizationId &&
p.ExcludeCurrentContext == false &&
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
p.Payload.Policy.Id == result.Id &&
p.Payload.Policy.Type == policyUpdate.Type &&
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
p.Payload.Policy.Data == policyUpdate.Data));
}
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_SendsPushNotificationWithUpdatedPolicy(
[PolicyUpdate(PolicyType.SingleOrg)] PolicyUpdate policyUpdate,
[Policy(PolicyType.SingleOrg, false)] Policy currentPolicy)
{
var fakePolicyValidator = new FakeSingleOrgPolicyValidator();
fakePolicyValidator.ValidateAsyncMock(policyUpdate, null).Returns("");
var sutProvider = SutProviderFactory([fakePolicyValidator]);
currentPolicy.OrganizationId = policyUpdate.OrganizationId;
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policyUpdate.OrganizationId, policyUpdate.Type)
.Returns(currentPolicy);
ArrangeOrganization(sutProvider, policyUpdate);
sutProvider.GetDependency<IPolicyRepository>()
.GetManyByOrganizationIdAsync(policyUpdate.OrganizationId)
.Returns([currentPolicy]);
var result = await sutProvider.Sut.SaveAsync(policyUpdate);
await sutProvider.GetDependency<IPushNotificationService>().Received(1)
.PushAsync(Arg.Is<PushNotification<SyncPolicyPushNotification>>(p =>
p.Type == PushType.PolicyChanged &&
p.Target == NotificationTarget.Organization &&
p.TargetId == policyUpdate.OrganizationId &&
p.ExcludeCurrentContext == false &&
p.Payload.OrganizationId == policyUpdate.OrganizationId &&
p.Payload.Policy.Id == result.Id &&
p.Payload.Policy.Type == policyUpdate.Type &&
p.Payload.Policy.Enabled == policyUpdate.Enabled &&
p.Payload.Policy.Data == policyUpdate.Data));
}
/// <summary>
/// Returns a new SutProvider with the PolicyValidators registered in the Sut.
/// </summary>

View File

@@ -225,6 +225,30 @@ public class HubHelpersTest
.Group(Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task SendNotificationToHubAsync_PolicyChanged_SentToOrganizationGroup(
SutProvider<HubHelpers> sutProvider,
SyncPolicyPushNotification notification,
string contextId,
CancellationToken cancellationToken)
{
var json = ToNotificationJson(notification, PushType.PolicyChanged, contextId);
await sutProvider.Sut.SendNotificationToHubAsync(json, cancellationToken);
sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
await sutProvider.GetDependency<IHubContext<NotificationsHub>>().Clients.Received(1)
.Group($"Organization_{notification.OrganizationId}")
.Received(1)
.SendCoreAsync("ReceiveMessage", Arg.Is<object?[]>(objects =>
objects.Length == 1 && AssertSyncPolicyPushNotification(notification, objects[0],
PushType.PolicyChanged, contextId)),
cancellationToken);
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0).User(Arg.Any<string>());
sutProvider.GetDependency<IHubContext<AnonymousNotificationsHub>>().Clients.Received(0)
.Group(Arg.Any<string>());
}
private static string ToNotificationJson(object payload, PushType type, string contextId)
{
var notification = new PushNotificationData<object>(type, payload, contextId);
@@ -247,4 +271,20 @@ public class HubHelpersTest
expected.ClientType == pushNotificationData.Payload.ClientType &&
expected.RevisionDate == pushNotificationData.Payload.RevisionDate;
}
private static bool AssertSyncPolicyPushNotification(SyncPolicyPushNotification expected, object? actual,
PushType type, string contextId)
{
if (actual is not PushNotificationData<SyncPolicyPushNotification> pushNotificationData)
{
return false;
}
return pushNotificationData.Type == type &&
pushNotificationData.ContextId == contextId &&
expected.OrganizationId == pushNotificationData.Payload.OrganizationId &&
expected.Policy.Id == pushNotificationData.Payload.Policy.Id &&
expected.Policy.Type == pushNotificationData.Payload.Policy.Type &&
expected.Policy.Enabled == pushNotificationData.Payload.Policy.Enabled;
}
}