1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 05:00:19 -05:00

[PM-17168] Sync organization user revoked/restored status immediately via push notification (#5330)

* [PM-17168] Add push notification for revoked and restored organization users

* Add feature flag for push notification on user revoke/restore actions

* Add tests for user revocation and restoration with push sync feature flag enabled
This commit is contained in:
Rui Tomé 2025-02-06 10:28:12 +00:00 committed by GitHub
parent 1c3ea1151c
commit a12b61cc9e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 230 additions and 66 deletions

View File

@ -1951,6 +1951,11 @@ public class OrganizationService : IOrganizationService
await RepositoryRevokeUserAsync(organizationUser); await RepositoryRevokeUserAsync(organizationUser);
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
} }
public async Task RevokeUserAsync(OrganizationUser organizationUser, public async Task RevokeUserAsync(OrganizationUser organizationUser,
@ -1958,6 +1963,11 @@ public class OrganizationService : IOrganizationService
{ {
await RepositoryRevokeUserAsync(organizationUser); await RepositoryRevokeUserAsync(organizationUser);
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, systemUser); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, systemUser);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
} }
private async Task RepositoryRevokeUserAsync(OrganizationUser organizationUser) private async Task RepositoryRevokeUserAsync(OrganizationUser organizationUser)
@ -2023,6 +2033,10 @@ public class OrganizationService : IOrganizationService
await _organizationUserRepository.RevokeAsync(organizationUser.Id); await _organizationUserRepository.RevokeAsync(organizationUser.Id);
organizationUser.Status = OrganizationUserStatusType.Revoked; organizationUser.Status = OrganizationUserStatusType.Revoked;
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
result.Add(Tuple.Create(organizationUser, "")); result.Add(Tuple.Create(organizationUser, ""));
} }
@ -2050,12 +2064,22 @@ public class OrganizationService : IOrganizationService
await RepositoryRestoreUserAsync(organizationUser); await RepositoryRestoreUserAsync(organizationUser);
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
} }
public async Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser) public async Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser)
{ {
await RepositoryRestoreUserAsync(organizationUser); await RepositoryRestoreUserAsync(organizationUser);
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, systemUser); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, systemUser);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
} }
private async Task RepositoryRestoreUserAsync(OrganizationUser organizationUser) private async Task RepositoryRestoreUserAsync(OrganizationUser organizationUser)
@ -2147,6 +2171,10 @@ public class OrganizationService : IOrganizationService
await _organizationUserRepository.RestoreAsync(organizationUser.Id, status); await _organizationUserRepository.RestoreAsync(organizationUser.Id, status);
organizationUser.Status = status; organizationUser.Status = status;
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
if (_featureService.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore) && organizationUser.UserId.HasValue)
{
await _pushNotificationService.PushSyncOrgKeysAsync(organizationUser.UserId.Value);
}
result.Add(Tuple.Create(organizationUser, "")); result.Add(Tuple.Create(organizationUser, ""));
} }

View File

@ -108,6 +108,7 @@ public static class FeatureFlagKeys
public const string IntegrationPage = "pm-14505-admin-console-integration-page"; public const string IntegrationPage = "pm-14505-admin-console-integration-page";
public const string DeviceApprovalRequestAdminNotifications = "pm-15637-device-approval-request-admin-notifications"; public const string DeviceApprovalRequestAdminNotifications = "pm-15637-device-approval-request-admin-notifications";
public const string LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission"; public const string LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission";
public const string PushSyncOrgKeysOnRevokeRestore = "pm-17168-push-sync-org-keys-on-revoke-restore";
/* Tools Team */ /* Tools Team */
public const string ItemShare = "item-share"; public const string ItemShare = "item-share";

View File

@ -19,6 +19,7 @@ using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Mail; using Bit.Core.Models.Mail;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Settings; using Bit.Core.Settings;
@ -1450,76 +1451,174 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider) [OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
{ {
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id); await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id);
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RevokeAsync(organizationUser.Id);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
} }
[Theory, BitAutoData]
public async Task RevokeUser_WithPushSyncOrgKeysOnRevokeRestoreEnabled_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
{
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore)
.Returns(true);
await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.RevokeAsync(organizationUser.Id);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
await sutProvider.GetDependency<IPushNotificationService>()
.Received(1)
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value);
}
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider) public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
{ {
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser); await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser);
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RevokeAsync(organizationUser.Id);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser);
} }
[Theory, BitAutoData]
public async Task RevokeUser_WithEventSystemUser_WithPushSyncOrgKeysOnRevokeRestoreEnabled_Success(Organization organization, [OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
{
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore)
.Returns(true);
await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.RevokeAsync(organizationUser.Id);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser);
await sutProvider.GetDependency<IPushNotificationService>()
.Received(1)
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value);
}
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task RestoreUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, public async Task RestoreUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider) [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
{ {
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id); await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
} }
[Theory, BitAutoData]
public async Task RestoreUser_WithPushSyncOrgKeysOnRevokeRestoreEnabled_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
{
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore)
.Returns(true);
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
await sutProvider.GetDependency<IPushNotificationService>()
.Received(1)
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value);
}
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider) public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
{ {
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
await sutProvider.Sut.RestoreUserAsync(organizationUser, eventSystemUser); await sutProvider.Sut.RestoreUserAsync(organizationUser, eventSystemUser);
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser);
} }
[Theory, BitAutoData]
public async Task RestoreUser_WithEventSystemUser_WithPushSyncOrgKeysOnRevokeRestoreEnabled_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
{
RestoreRevokeUser_Setup(organization, null, organizationUser, sutProvider);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.PushSyncOrgKeysOnRevokeRestore)
.Returns(true);
await sutProvider.Sut.RestoreUserAsync(organizationUser, eventSystemUser);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser);
await sutProvider.GetDependency<IPushNotificationService>()
.Received(1)
.PushSyncOrgKeysAsync(organizationUser.UserId!.Value);
}
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task RestoreUser_RestoreThemselves_Fails(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, public async Task RestoreUser_RestoreThemselves_Fails(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider) [OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
{ {
organizationUser.UserId = owner.Id; organizationUser.UserId = owner.Id;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id)); () => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
Assert.Contains("you cannot restore yourself", exception.Message.ToLowerInvariant()); Assert.Contains("you cannot restore yourself", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory] [Theory]
@ -1531,17 +1630,21 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{ {
restoringUser.Type = restoringUserType; restoringUser.Type = restoringUserType;
RestoreRevokeUser_Setup(organization, restoringUser, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, restoringUser, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, restoringUser.Id)); () => sutProvider.Sut.RestoreUserAsync(organizationUser, restoringUser.Id));
Assert.Contains("only owners can restore other owners", exception.Message.ToLowerInvariant()); Assert.Contains("only owners can restore other owners", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory] [Theory]
@ -1553,17 +1656,21 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{ {
organizationUser.Status = userStatus; organizationUser.Status = userStatus;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id)); () => sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id));
Assert.Contains("already active", exception.Message.ToLowerInvariant()); Assert.Contains("already active", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1575,8 +1682,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{ {
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.AnyPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>()) .AnyPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
@ -1591,9 +1696,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1610,8 +1721,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, false) }); .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, false) });
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
@ -1626,9 +1735,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1640,8 +1755,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{ {
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
@ -1652,8 +1765,11 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id); await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
} }
@ -1668,10 +1784,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
secondOrganizationUser.UserId = organizationUser.UserId; secondOrganizationUser.UserId = organizationUser.UserId;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
organizationUserRepository.GetManyByUserAsync(organizationUser.UserId.Value).Returns(new[] { organizationUser, secondOrganizationUser }); sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByUserAsync(organizationUser.UserId.Value)
.Returns(new[] { organizationUser, secondOrganizationUser });
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
.Returns(new[] .Returns(new[]
@ -1688,9 +1804,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com is not compliant with the single organization policy", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com is not compliant with the single organization policy", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1704,11 +1826,8 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
secondOrganizationUser.UserId = organizationUser.UserId; secondOrganizationUser.UserId = organizationUser.UserId;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
twoFactorIsEnabledQuery sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value))) .TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) }); .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) });
@ -1725,9 +1844,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com belongs to an organization that doesn't allow them to join multiple organizations", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1741,10 +1866,10 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
secondOrganizationUser.UserId = organizationUser.UserId; secondOrganizationUser.UserId = organizationUser.UserId;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
organizationUserRepository.GetManyByUserAsync(organizationUser.UserId.Value).Returns(new[] { organizationUser, secondOrganizationUser }); sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByUserAsync(organizationUser.UserId.Value)
.Returns(new[] { organizationUser, secondOrganizationUser });
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.SingleOrg, Arg.Any<OrganizationUserStatusType>())
.Returns(new[] .Returns(new[]
@ -1768,9 +1893,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com is not compliant with the single organization and two-step login polciy", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com is not compliant with the single organization and two-step login polciy", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1783,8 +1914,6 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
organizationUser.Email = null; organizationUser.Email = null;
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
@ -1799,9 +1928,15 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant()); Assert.Contains("test@bitwarden.com is not compliant with the two-step login policy", exception.Message.ToLowerInvariant());
await organizationUserRepository.DidNotReceiveWithAnyArgs().RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>()); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.RestoreAsync(Arg.Any<Guid>(), Arg.Any<OrganizationUserStatusType>());
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<EventSystemUser>());
await sutProvider.GetDependency<IPushNotificationService>()
.DidNotReceiveWithAnyArgs()
.PushSyncOrgKeysAsync(Arg.Any<Guid>());
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -1813,22 +1948,22 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{ {
organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke organizationUser.Email = null; // this is required to mock that the user as had already been confirmed before the revoke
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider); RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
var eventService = sutProvider.GetDependency<IEventService>();
var twoFactorIsEnabledQuery = sutProvider.GetDependency<ITwoFactorIsEnabledQuery>();
sutProvider.GetDependency<IPolicyService>() sutProvider.GetDependency<IPolicyService>()
.GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>()) .GetPoliciesApplicableToUserAsync(organizationUser.UserId.Value, PolicyType.TwoFactorAuthentication, Arg.Any<OrganizationUserStatusType>())
.Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } }); .Returns(new[] { new OrganizationUserPolicyDetails { OrganizationId = organizationUser.OrganizationId, PolicyType = PolicyType.TwoFactorAuthentication } });
twoFactorIsEnabledQuery sutProvider.GetDependency<ITwoFactorIsEnabledQuery>()
.TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value))) .TwoFactorIsEnabledAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.UserId.Value)))
.Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) }); .Returns(new List<(Guid userId, bool twoFactorIsEnabled)>() { (organizationUser.UserId.Value, true) });
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id); await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id);
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed); await sutProvider.GetDependency<IOrganizationUserRepository>()
await eventService.Received() .Received(1)
.RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Confirmed);
await sutProvider.GetDependency<IEventService>()
.Received(1)
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored); .LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
} }