diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index c83fce7622..736fb30dde 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -899,8 +899,9 @@ public class OrganizationsController : Controller var orgUsers = await _organizationUserRepository.GetManyByOrganizationAsync(id, null); await Task.WhenAll(orgUsers .Where(ou => ou.UserId.HasValue && + ou.Status == OrganizationUserStatusType.Confirmed && ou.Type is OrganizationUserType.Admin or OrganizationUserType.Owner) - .Select(ou => _pushNotificationService.PushSyncVaultAsync(ou.UserId.Value))); + .Select(ou => _pushNotificationService.PushSyncOrganizationsAsync(ou.UserId.Value))); } private async Task TryGrantOwnerAccessToSecretsManagerAsync(Guid organizationId, Guid userId) diff --git a/src/Core/Enums/PushType.cs b/src/Core/Enums/PushType.cs index 82029e9213..9dbef7b8e2 100644 --- a/src/Core/Enums/PushType.cs +++ b/src/Core/Enums/PushType.cs @@ -23,4 +23,6 @@ public enum PushType : byte AuthRequest = 15, AuthRequestResponse = 16, + + SyncOrganizations = 17, } diff --git a/src/Core/Services/IPushNotificationService.cs b/src/Core/Services/IPushNotificationService.cs index fcb1c3fdae..29a20239d1 100644 --- a/src/Core/Services/IPushNotificationService.cs +++ b/src/Core/Services/IPushNotificationService.cs @@ -15,6 +15,7 @@ public interface IPushNotificationService Task PushSyncFolderDeleteAsync(Folder folder); Task PushSyncCiphersAsync(Guid userId); Task PushSyncVaultAsync(Guid userId); + Task PushSyncOrganizationsAsync(Guid userId); Task PushSyncOrgKeysAsync(Guid userId); Task PushSyncSettingsAsync(Guid userId); Task PushLogOutAsync(Guid userId, bool excludeCurrentContextFromPush = false); diff --git a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs index 1c0b1cf600..1e4a7314c4 100644 --- a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs +++ b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs @@ -106,6 +106,11 @@ public class AzureQueuePushNotificationService : IPushNotificationService await PushUserAsync(userId, PushType.SyncVault); } + public async Task PushSyncOrganizationsAsync(Guid userId) + { + await PushUserAsync(userId, PushType.SyncOrganizations); + } + public async Task PushSyncOrgKeysAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncOrgKeys); diff --git a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs index 7b878bab6a..b683c05d0f 100644 --- a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs +++ b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs @@ -105,6 +105,12 @@ public class MultiServicePushNotificationService : IPushNotificationService return Task.FromResult(0); } + public Task PushSyncOrganizationsAsync(Guid userId) + { + PushToServices((s) => s.PushSyncOrganizationsAsync(userId)); + return Task.FromResult(0); + } + public Task PushSyncOrgKeysAsync(Guid userId) { PushToServices((s) => s.PushSyncOrgKeysAsync(userId)); diff --git a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs index ec66660dff..96c50ca93a 100644 --- a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs @@ -117,6 +117,11 @@ public class NotificationHubPushNotificationService : IPushNotificationService await PushUserAsync(userId, PushType.SyncVault); } + public async Task PushSyncOrganizationsAsync(Guid userId) + { + await PushUserAsync(userId, PushType.SyncOrganizations); + } + public async Task PushSyncOrgKeysAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncOrgKeys); diff --git a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs index 87dc9b694b..9ec1eb31d4 100644 --- a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs @@ -113,6 +113,11 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService await PushUserAsync(userId, PushType.SyncVault); } + public async Task PushSyncOrganizationsAsync(Guid userId) + { + await PushUserAsync(userId, PushType.SyncOrganizations); + } + public async Task PushSyncOrgKeysAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncOrgKeys); diff --git a/src/Core/Services/Implementations/RelayPushNotificationService.cs b/src/Core/Services/Implementations/RelayPushNotificationService.cs index 4e0858610b..6cfc0c0a61 100644 --- a/src/Core/Services/Implementations/RelayPushNotificationService.cs +++ b/src/Core/Services/Implementations/RelayPushNotificationService.cs @@ -114,6 +114,11 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti await PushUserAsync(userId, PushType.SyncVault); } + public async Task PushSyncOrganizationsAsync(Guid userId) + { + await PushUserAsync(userId, PushType.SyncOrganizations); + } + public async Task PushSyncOrgKeysAsync(Guid userId) { await PushUserAsync(userId, PushType.SyncOrgKeys); diff --git a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs index dac84f256b..d4eff93ef6 100644 --- a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs +++ b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs @@ -42,6 +42,11 @@ public class NoopPushNotificationService : IPushNotificationService return Task.FromResult(0); } + public Task PushSyncOrganizationsAsync(Guid userId) + { + return Task.FromResult(0); + } + public Task PushSyncOrgKeysAsync(Guid userId) { return Task.FromResult(0); diff --git a/src/Notifications/HubHelpers.cs b/src/Notifications/HubHelpers.cs index 71319dd559..8463e1f34a 100644 --- a/src/Notifications/HubHelpers.cs +++ b/src/Notifications/HubHelpers.cs @@ -50,6 +50,7 @@ public static class HubHelpers break; case PushType.SyncCiphers: case PushType.SyncVault: + case PushType.SyncOrganizations: case PushType.SyncOrgKeys: case PushType.SyncSettings: case PushType.LogOut: diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs index 983470d6fb..fdbcc17e46 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationsControllerTests.cs @@ -377,14 +377,15 @@ public class OrganizationsControllerTests : IDisposable public async Task EnableCollectionEnhancements_Success(Organization organization) { organization.FlexibleCollections = false; - var admin = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.Admin }; - var owner = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.Owner }; - var user = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.User }; + var admin = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.Admin, Status = OrganizationUserStatusType.Confirmed }; + var owner = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.Owner, Status = OrganizationUserStatusType.Confirmed }; + var user = new OrganizationUser { UserId = Guid.NewGuid(), Type = OrganizationUserType.User, Status = OrganizationUserStatusType.Confirmed }; var invited = new OrganizationUser { UserId = null, Type = OrganizationUserType.Admin, - Email = "invited@example.com" + Email = "invited@example.com", + Status = OrganizationUserStatusType.Invited }; var orgUsers = new List { admin, owner, user, invited }; @@ -395,9 +396,10 @@ public class OrganizationsControllerTests : IDisposable await _sut.EnableCollectionEnhancements(organization.Id); await _organizationEnableCollectionEnhancementsCommand.Received(1).EnableCollectionEnhancements(organization); - await _pushNotificationService.Received(1).PushSyncVaultAsync(admin.UserId.Value); - await _pushNotificationService.Received(1).PushSyncVaultAsync(owner.UserId.Value); - await _pushNotificationService.DidNotReceive().PushSyncVaultAsync(user.UserId.Value); + await _pushNotificationService.Received(1).PushSyncOrganizationsAsync(admin.UserId.Value); + await _pushNotificationService.Received(1).PushSyncOrganizationsAsync(owner.UserId.Value); + await _pushNotificationService.DidNotReceive().PushSyncOrganizationsAsync(user.UserId.Value); + // Invited orgUser does not have a UserId we can use to assert here, but sut will throw if that null isn't handled } [Theory, AutoData] @@ -410,6 +412,6 @@ public class OrganizationsControllerTests : IDisposable await Assert.ThrowsAsync(async () => await _sut.EnableCollectionEnhancements(organization.Id)); await _organizationEnableCollectionEnhancementsCommand.DidNotReceiveWithAnyArgs().EnableCollectionEnhancements(Arg.Any()); - await _pushNotificationService.DidNotReceiveWithAnyArgs().PushSyncVaultAsync(Arg.Any()); + await _pushNotificationService.DidNotReceiveWithAnyArgs().PushSyncOrganizationsAsync(Arg.Any()); } }