From 35fab48ad9da4c4f6a17c5abe64be24f5728941d Mon Sep 17 00:00:00 2001 From: Maciej Zieniuk Date: Wed, 20 Nov 2024 11:39:41 +0000 Subject: [PATCH] PM-10600: Organization push notifications not sending to mobile device from self-hosted. Self-hosted instance uses relay to register the mobile device against Bitwarden Cloud Api. Only the self-hosted server knows client's organization membership, which means it needs to pass in the organization id's information to the relay. Similarly, for Bitwarden Cloud, the organizaton id will come directly from the server. --- src/Api/Controllers/PushController.cs | 2 +- .../Request/PushRegistrationRequestModel.cs | 1 + .../NotificationHubPushRegistrationService.cs | 28 ++-- src/Core/Services/IPushRegistrationService.cs | 2 +- .../Services/Implementations/DeviceService.cs | 13 +- .../RelayPushRegistrationService.cs | 5 +- .../NoopPushRegistrationService.cs | 2 +- ...ficationHubPushRegistrationServiceTests.cs | 125 ++++++++---------- test/Core.Test/Services/DeviceServiceTests.cs | 102 +++++++++----- 9 files changed, 162 insertions(+), 118 deletions(-) diff --git a/src/Api/Controllers/PushController.cs b/src/Api/Controllers/PushController.cs index f41035b517..d760aaa5d7 100644 --- a/src/Api/Controllers/PushController.cs +++ b/src/Api/Controllers/PushController.cs @@ -39,7 +39,7 @@ public class PushController : Controller { CheckUsage(); await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId), - Prefix(model.UserId), Prefix(model.Identifier), model.Type); + Prefix(model.UserId), Prefix(model.Identifier), model.Type, model.OrganizationIds); } [HttpPost("delete")] diff --git a/src/Core/Models/Api/Request/PushRegistrationRequestModel.cs b/src/Core/Models/Api/Request/PushRegistrationRequestModel.cs index 580c1c3b60..ee787dd083 100644 --- a/src/Core/Models/Api/Request/PushRegistrationRequestModel.cs +++ b/src/Core/Models/Api/Request/PushRegistrationRequestModel.cs @@ -15,4 +15,5 @@ public class PushRegistrationRequestModel public DeviceType Type { get; set; } [Required] public string Identifier { get; set; } + public IEnumerable OrganizationIds { get; set; } } diff --git a/src/Core/NotificationHub/NotificationHubPushRegistrationService.cs b/src/Core/NotificationHub/NotificationHubPushRegistrationService.cs index f12d7d7fbf..30c3070577 100644 --- a/src/Core/NotificationHub/NotificationHubPushRegistrationService.cs +++ b/src/Core/NotificationHub/NotificationHubPushRegistrationService.cs @@ -11,20 +11,17 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService { private readonly IInstallationDeviceRepository _installationDeviceRepository; private readonly INotificationHubPool _notificationHubPool; - private readonly IOrganizationUserRepository _organizationUserRepository; public NotificationHubPushRegistrationService( IInstallationDeviceRepository installationDeviceRepository, - INotificationHubPool notificationHubPool, - IOrganizationUserRepository organizationUserRepository) + INotificationHubPool notificationHubPool) { _installationDeviceRepository = installationDeviceRepository; _notificationHubPool = notificationHubPool; - _organizationUserRepository = organizationUserRepository; } public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId, - string identifier, DeviceType type) + string identifier, DeviceType type, IEnumerable organizationIds) { if (string.IsNullOrWhiteSpace(pushToken)) { @@ -47,10 +44,10 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService installation.Tags.Add("deviceIdentifier:" + identifier); } - foreach (var organizationUserDetails in await _organizationUserRepository.GetManyDetailsByUserAsync( - Guid.Parse(userId), OrganizationUserStatusType.Confirmed)) + var organizationIdsList = organizationIds.ToList(); + foreach (var organizationId in organizationIdsList) { - installation.Tags.Add($"organizationId:{organizationUserDetails.OrganizationId}"); + installation.Tags.Add($"organizationId:{organizationId}"); } string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null; @@ -82,10 +79,12 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService break; } - BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier, clientType); - BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier, clientType); + BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier, clientType, + organizationIdsList); + BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier, clientType, + organizationIdsList); BuildInstallationTemplate(installation, "badgeMessage", badgeMessageTemplate ?? messageTemplate, - userId, identifier, clientType); + userId, identifier, clientType, organizationIdsList); await ClientFor(GetComb(deviceId)).CreateOrUpdateInstallationAsync(installation); if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId)) @@ -95,7 +94,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService } private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody, - string userId, string identifier, ClientType clientType) + string userId, string identifier, ClientType clientType, List organizationIds) { if (templateBody == null) { @@ -118,6 +117,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService template.Tags.Add($"{fullTemplateId}_deviceIdentifier:{identifier}"); } + foreach (var organizationId in organizationIds) + { + template.Tags.Add($"organizationId:{organizationId}"); + } + installation.Templates.Add(fullTemplateId, template); } diff --git a/src/Core/Services/IPushRegistrationService.cs b/src/Core/Services/IPushRegistrationService.cs index 985246de0c..304387581b 100644 --- a/src/Core/Services/IPushRegistrationService.cs +++ b/src/Core/Services/IPushRegistrationService.cs @@ -5,7 +5,7 @@ namespace Bit.Core.Services; public interface IPushRegistrationService { Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId, - string identifier, DeviceType type); + string identifier, DeviceType type, IEnumerable organizationIds); Task DeleteRegistrationAsync(string deviceId); Task AddUserRegistrationOrganizationAsync(IEnumerable deviceIds, string organizationId); Task DeleteUserRegistrationOrganizationAsync(IEnumerable deviceIds, string organizationId); diff --git a/src/Core/Services/Implementations/DeviceService.cs b/src/Core/Services/Implementations/DeviceService.cs index 638e4c5e07..cc45bf62a0 100644 --- a/src/Core/Services/Implementations/DeviceService.cs +++ b/src/Core/Services/Implementations/DeviceService.cs @@ -1,6 +1,7 @@ using Bit.Core.Auth.Models.Api.Request; using Bit.Core.Auth.Utilities; using Bit.Core.Entities; +using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Repositories; @@ -10,13 +11,16 @@ public class DeviceService : IDeviceService { private readonly IDeviceRepository _deviceRepository; private readonly IPushRegistrationService _pushRegistrationService; + private readonly IOrganizationUserRepository _organizationUserRepository; public DeviceService( IDeviceRepository deviceRepository, - IPushRegistrationService pushRegistrationService) + IPushRegistrationService pushRegistrationService, + IOrganizationUserRepository organizationUserRepository) { _deviceRepository = deviceRepository; _pushRegistrationService = pushRegistrationService; + _organizationUserRepository = organizationUserRepository; } public async Task SaveAsync(Device device) @@ -31,8 +35,13 @@ public class DeviceService : IDeviceService await _deviceRepository.ReplaceAsync(device); } + var organizationIdsString = + (await _organizationUserRepository.GetManyDetailsByUserAsync(device.UserId, + OrganizationUserStatusType.Confirmed)) + .Select(ou => ou.OrganizationId.ToString()); + await _pushRegistrationService.CreateOrUpdateRegistrationAsync(device.PushToken, device.Id.ToString(), - device.UserId.ToString(), device.Identifier, device.Type); + device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString); } public async Task ClearTokenAsync(Device device) diff --git a/src/Core/Services/Implementations/RelayPushRegistrationService.cs b/src/Core/Services/Implementations/RelayPushRegistrationService.cs index d0f7736e98..3000f1545b 100644 --- a/src/Core/Services/Implementations/RelayPushRegistrationService.cs +++ b/src/Core/Services/Implementations/RelayPushRegistrationService.cs @@ -25,7 +25,7 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi } public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId, - string identifier, DeviceType type) + string identifier, DeviceType type, IEnumerable organizationIds) { var requestModel = new PushRegistrationRequestModel { @@ -33,7 +33,8 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi Identifier = identifier, PushToken = pushToken, Type = type, - UserId = userId + UserId = userId, + OrganizationIds = organizationIds }; await SendAsync(HttpMethod.Post, "push/register", requestModel); } diff --git a/src/Core/Services/NoopImplementations/NoopPushRegistrationService.cs b/src/Core/Services/NoopImplementations/NoopPushRegistrationService.cs index f6279c9467..4db799580b 100644 --- a/src/Core/Services/NoopImplementations/NoopPushRegistrationService.cs +++ b/src/Core/Services/NoopImplementations/NoopPushRegistrationService.cs @@ -10,7 +10,7 @@ public class NoopPushRegistrationService : IPushRegistrationService } public Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId, - string identifier, DeviceType type) + string identifier, DeviceType type, IEnumerable organizationIds) { return Task.FromResult(0); } diff --git a/test/Core.Test/NotificationHub/NotificationHubPushRegistrationServiceTests.cs b/test/Core.Test/NotificationHub/NotificationHubPushRegistrationServiceTests.cs index 9c63c2c1b9..d51df9c882 100644 --- a/test/Core.Test/NotificationHub/NotificationHubPushRegistrationServiceTests.cs +++ b/test/Core.Test/NotificationHub/NotificationHubPushRegistrationServiceTests.cs @@ -1,8 +1,6 @@ #nullable enable using Bit.Core.Enums; -using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.NotificationHub; -using Bit.Core.Repositories; using Bit.Core.Utilities; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; @@ -19,11 +17,12 @@ public class NotificationHubPushRegistrationServiceTests [BitAutoData([null])] [BitAutoData("")] [BitAutoData(" ")] - public async void CreateOrUpdateRegistrationAsync_PushTokenNullOrEmpty_InstallationNotCreated(string? pushToken, - SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier) + public async Task CreateOrUpdateRegistrationAsync_PushTokenNullOrEmpty_InstallationNotCreated(string? pushToken, + SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier, + Guid organizationId) { await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifier.ToString(), DeviceType.Android); + identifier.ToString(), DeviceType.Android, [organizationId.ToString()]); sutProvider.GetDependency() .Received(0) @@ -31,10 +30,13 @@ public class NotificationHubPushRegistrationServiceTests } [Theory] - [BitAutoData(false)] - [BitAutoData(true)] - public async void CreateOrUpdateRegistrationAsync_DeviceTypeAndroid_InstallationCreated(bool identifierNull, - SutProvider sutProvider, Guid deviceId, Guid userId, Guid? identifier) + [BitAutoData(false, false)] + [BitAutoData(false, true)] + [BitAutoData(true, false)] + [BitAutoData(true, true)] + public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroid_InstallationCreated(bool identifierNull, + bool partOfOrganizationId, SutProvider sutProvider, Guid deviceId, + Guid userId, Guid? identifier, Guid organizationId) { var notificationHubClient = Substitute.For(); sutProvider.GetDependency().ClientFor(Arg.Any()).Returns(notificationHubClient); @@ -42,7 +44,8 @@ public class NotificationHubPushRegistrationServiceTests var pushToken = "test push token"; await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifierNull ? null : identifier.ToString(), DeviceType.Android); + identifierNull ? null : identifier.ToString(), DeviceType.Android, + partOfOrganizationId ? [organizationId.ToString()] : []); sutProvider.GetDependency() .Received(1) @@ -53,10 +56,10 @@ public class NotificationHubPushRegistrationServiceTests installation.InstallationId == deviceId.ToString() && installation.PushChannel == pushToken && installation.Platform == NotificationPlatform.FcmV1 && - installation.Tags.Count == (identifierNull ? 2 : 3) && installation.Tags.Contains($"userId:{userId}") && installation.Tags.Contains("clientType:Mobile") && (identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) && + (!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) && installation.Templates.Count == 3)); await notificationHubClient .Received(1) @@ -68,7 +71,8 @@ public class NotificationHubPushRegistrationServiceTests "template:payload", $"template:payload_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -80,7 +84,8 @@ public class NotificationHubPushRegistrationServiceTests "template:message", $"template:message_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:message_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:message_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -92,15 +97,19 @@ public class NotificationHubPushRegistrationServiceTests "template:badgeMessage", $"template:badgeMessage_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); } [Theory] - [BitAutoData(false)] - [BitAutoData(true)] - public async void CreateOrUpdateRegistrationAsync_DeviceTypeIOS_InstallationCreated(bool identifierNull, - SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier) + [BitAutoData(false, false)] + [BitAutoData(false, true)] + [BitAutoData(true, false)] + [BitAutoData(true, true)] + public async Task CreateOrUpdateRegistrationAsync_DeviceTypeIOS_InstallationCreated(bool identifierNull, + bool partOfOrganizationId, SutProvider sutProvider, Guid deviceId, + Guid userId, Guid identifier, Guid organizationId) { var notificationHubClient = Substitute.For(); sutProvider.GetDependency().ClientFor(Arg.Any()).Returns(notificationHubClient); @@ -108,7 +117,8 @@ public class NotificationHubPushRegistrationServiceTests var pushToken = "test push token"; await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifierNull ? null : identifier.ToString(), DeviceType.iOS); + identifierNull ? null : identifier.ToString(), DeviceType.iOS, + partOfOrganizationId ? [organizationId.ToString()] : []); sutProvider.GetDependency() .Received(1) @@ -119,10 +129,10 @@ public class NotificationHubPushRegistrationServiceTests installation.InstallationId == deviceId.ToString() && installation.PushChannel == pushToken && installation.Platform == NotificationPlatform.Apns && - installation.Tags.Count == (identifierNull ? 2 : 3) && installation.Tags.Contains($"userId:{userId}") && installation.Tags.Contains("clientType:Mobile") && (identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) && + (!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) && installation.Templates.Count == 3)); await notificationHubClient .Received(1) @@ -134,7 +144,8 @@ public class NotificationHubPushRegistrationServiceTests "template:payload", $"template:payload_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -146,7 +157,8 @@ public class NotificationHubPushRegistrationServiceTests "template:message", $"template:message_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:message_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:message_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -158,15 +170,19 @@ public class NotificationHubPushRegistrationServiceTests "template:badgeMessage", $"template:badgeMessage_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); } [Theory] - [BitAutoData(false)] - [BitAutoData(true)] - public async void CreateOrUpdateRegistrationAsync_DeviceTypeAndroidAmazon_InstallationCreated(bool identifierNull, - SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier) + [BitAutoData(false, false)] + [BitAutoData(false, true)] + [BitAutoData(true, false)] + [BitAutoData(true, true)] + public async Task CreateOrUpdateRegistrationAsync_DeviceTypeAndroidAmazon_InstallationCreated(bool identifierNull, + bool partOfOrganizationId, SutProvider sutProvider, Guid deviceId, + Guid userId, Guid identifier, Guid organizationId) { var notificationHubClient = Substitute.For(); sutProvider.GetDependency().ClientFor(Arg.Any()).Returns(notificationHubClient); @@ -174,7 +190,8 @@ public class NotificationHubPushRegistrationServiceTests var pushToken = "test push token"; await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifierNull ? null : identifier.ToString(), DeviceType.AndroidAmazon); + identifierNull ? null : identifier.ToString(), DeviceType.AndroidAmazon, + partOfOrganizationId ? [organizationId.ToString()] : []); sutProvider.GetDependency() .Received(1) @@ -185,10 +202,10 @@ public class NotificationHubPushRegistrationServiceTests installation.InstallationId == deviceId.ToString() && installation.PushChannel == pushToken && installation.Platform == NotificationPlatform.Adm && - installation.Tags.Count == (identifierNull ? 2 : 3) && installation.Tags.Contains($"userId:{userId}") && installation.Tags.Contains("clientType:Mobile") && (identifierNull || installation.Tags.Contains($"deviceIdentifier:{identifier}")) && + (!partOfOrganizationId || installation.Tags.Contains($"organizationId:{organizationId}")) && installation.Templates.Count == 3)); await notificationHubClient .Received(1) @@ -200,7 +217,8 @@ public class NotificationHubPushRegistrationServiceTests "template:payload", $"template:payload_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:payload_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -212,7 +230,8 @@ public class NotificationHubPushRegistrationServiceTests "template:message", $"template:message_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:message_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:message_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); await notificationHubClient .Received(1) @@ -224,7 +243,8 @@ public class NotificationHubPushRegistrationServiceTests "template:badgeMessage", $"template:badgeMessage_userId:{userId}", "clientType:Mobile", - identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}" + identifierNull ? null : $"template:badgeMessage_deviceIdentifier:{identifier}", + partOfOrganizationId ? $"organizationId:{organizationId}" : null, }))); } @@ -232,8 +252,9 @@ public class NotificationHubPushRegistrationServiceTests [BitAutoData(DeviceType.ChromeBrowser)] [BitAutoData(DeviceType.ChromeExtension)] [BitAutoData(DeviceType.MacOsDesktop)] - public async void CreateOrUpdateRegistrationAsync_DeviceTypeNotMobile_InstallationCreated(DeviceType deviceType, - SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier) + public async Task CreateOrUpdateRegistrationAsync_DeviceTypeNotMobile_InstallationCreated(DeviceType deviceType, + SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier, + Guid organizationId) { var notificationHubClient = Substitute.For(); sutProvider.GetDependency().ClientFor(Arg.Any()).Returns(notificationHubClient); @@ -241,7 +262,7 @@ public class NotificationHubPushRegistrationServiceTests var pushToken = "test push token"; await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifier.ToString(), deviceType); + identifier.ToString(), deviceType, [organizationId.ToString()]); sutProvider.GetDependency() .Received(1) @@ -251,45 +272,13 @@ public class NotificationHubPushRegistrationServiceTests .CreateOrUpdateInstallationAsync(Arg.Is(installation => installation.InstallationId == deviceId.ToString() && installation.PushChannel == pushToken && - installation.Tags.Count == 3 && installation.Tags.Contains($"userId:{userId}") && installation.Tags.Contains($"clientType:{DeviceTypes.ToClientType(deviceType)}") && installation.Tags.Contains($"deviceIdentifier:{identifier}") && + installation.Tags.Contains($"organizationId:{organizationId}") && installation.Templates.Count == 0)); } - [Theory] - [BitAutoData] - public async void CreateOrUpdateRegistrationAsync_UserPartOfOrganization_InstallationCreated( - SutProvider sutProvider, Guid deviceId, Guid userId, Guid identifier, - OrganizationUserOrganizationDetails organizationDetails) - { - var notificationHubClient = Substitute.For(); - sutProvider.GetDependency().ClientFor(Arg.Any()).Returns(notificationHubClient); - sutProvider.GetDependency() - .GetManyDetailsByUserAsync(userId, OrganizationUserStatusType.Confirmed) - .Returns([organizationDetails]); - - var pushToken = "test push token"; - - await sutProvider.Sut.CreateOrUpdateRegistrationAsync(pushToken, deviceId.ToString(), userId.ToString(), - identifier.ToString(), DeviceType.ChromeBrowser); - - sutProvider.GetDependency() - .Received(1) - .ClientFor(deviceId); - await notificationHubClient - .Received(1) - .CreateOrUpdateInstallationAsync(Arg.Is(installation => - installation.InstallationId == deviceId.ToString() && - installation.PushChannel == pushToken && - installation.Tags.Count == 4 && - installation.Tags.Contains($"userId:{userId}") && - installation.Tags.Contains($"clientType:{DeviceTypes.ToClientType(DeviceType.ChromeBrowser)}") && - installation.Tags.Contains($"deviceIdentifier:{identifier}") && - installation.Tags.Contains($"organizationId:{organizationDetails.OrganizationId}"))); - } - private static bool MatchingInstallationTemplate(IDictionary templates, string key, string body, List tags) { diff --git a/test/Core.Test/Services/DeviceServiceTests.cs b/test/Core.Test/Services/DeviceServiceTests.cs index cb2aebc992..10aa8761fa 100644 --- a/test/Core.Test/Services/DeviceServiceTests.cs +++ b/test/Core.Test/Services/DeviceServiceTests.cs @@ -3,6 +3,7 @@ using Bit.Core.Auth.Models.Api.Request; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; @@ -15,15 +16,23 @@ namespace Bit.Core.Test.Services; [SutProviderCustomize] public class DeviceServiceTests { - [Fact] - public async Task DeviceSaveShouldUpdateRevisionDateAndPushRegistration() + [Theory] + [BitAutoData] + public async Task SaveAsync_IdProvided_UpdatedRevisionDateAndPushRegistration(Guid id, Guid userId, + Guid organizationId1, Guid organizationId2, + OrganizationUserOrganizationDetails organizationUserOrganizationDetails1, + OrganizationUserOrganizationDetails organizationUserOrganizationDetails2) { + organizationUserOrganizationDetails1.OrganizationId = organizationId1; + organizationUserOrganizationDetails2.OrganizationId = organizationId2; + var deviceRepo = Substitute.For(); var pushRepo = Substitute.For(); - var deviceService = new DeviceService(deviceRepo, pushRepo); + var organizationUserRepository = Substitute.For(); + organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any(), Arg.Any()) + .Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]); + var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository); - var id = Guid.NewGuid(); - var userId = Guid.NewGuid(); var device = new Device { Id = id, @@ -36,8 +45,53 @@ public class DeviceServiceTests await deviceService.SaveAsync(device); Assert.True(device.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1)); - await pushRepo.Received().CreateOrUpdateRegistrationAsync("testtoken", id.ToString(), - userId.ToString(), "testid", DeviceType.Android); + await pushRepo.Received(1).CreateOrUpdateRegistrationAsync("testtoken", id.ToString(), + userId.ToString(), "testid", DeviceType.Android, + Arg.Do>(organizationIds => + { + var organizationIdsList = organizationIds.ToList(); + Assert.Equal(2, organizationIdsList.Count); + Assert.Contains(organizationId1.ToString(), organizationIdsList); + Assert.Contains(organizationId2.ToString(), organizationIdsList); + })); + } + + [Theory] + [BitAutoData] + public async Task SaveAsync_IdNotProvided_CreatedAndPushRegistration(Guid userId, Guid organizationId1, + Guid organizationId2, + OrganizationUserOrganizationDetails organizationUserOrganizationDetails1, + OrganizationUserOrganizationDetails organizationUserOrganizationDetails2) + { + organizationUserOrganizationDetails1.OrganizationId = organizationId1; + organizationUserOrganizationDetails2.OrganizationId = organizationId2; + + var deviceRepo = Substitute.For(); + var pushRepo = Substitute.For(); + var organizationUserRepository = Substitute.For(); + organizationUserRepository.GetManyDetailsByUserAsync(Arg.Any(), Arg.Any()) + .Returns([organizationUserOrganizationDetails1, organizationUserOrganizationDetails2]); + var deviceService = new DeviceService(deviceRepo, pushRepo, organizationUserRepository); + + var device = new Device + { + Name = "test device", + Type = DeviceType.Android, + UserId = userId, + PushToken = "testtoken", + Identifier = "testid" + }; + await deviceService.SaveAsync(device); + + await pushRepo.Received(1).CreateOrUpdateRegistrationAsync("testtoken", + Arg.Do(id => Guid.TryParse(id, out var _)), userId.ToString(), "testid", DeviceType.Android, + Arg.Do>(organizationIds => + { + var organizationIdsList = organizationIds.ToList(); + Assert.Equal(2, organizationIdsList.Count); + Assert.Contains(organizationId1.ToString(), organizationIdsList); + Assert.Contains(organizationId2.ToString(), organizationIdsList); + })); } /// @@ -61,12 +115,7 @@ public class DeviceServiceTests sutProvider.GetDependency() .GetManyByUserIdAsync(currentUserId) - .Returns(new List - { - deviceOne, - deviceTwo, - deviceThree, - }); + .Returns(new List { deviceOne, deviceTwo, deviceThree, }); var currentDeviceModel = new DeviceKeysUpdateRequestModel { @@ -84,7 +133,8 @@ public class DeviceServiceTests }, }; - await sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, alteredDeviceModels); + await sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, + alteredDeviceModels); // Updating trust, "current" or "other" only needs to change the EncryptedPublicKey & EncryptedUserKey await sutProvider.GetDependency() @@ -148,11 +198,7 @@ public class DeviceServiceTests sutProvider.GetDependency() .GetManyByUserIdAsync(currentUserId) - .Returns(new List - { - deviceOne, - deviceTwo, - }); + .Returns(new List { deviceOne, deviceTwo, }); var currentDeviceModel = new DeviceKeysUpdateRequestModel { @@ -170,7 +216,8 @@ public class DeviceServiceTests }, }; - await sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, alteredDeviceModels); + await sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, + alteredDeviceModels); // Check that UpsertAsync was called for the trusted device await sutProvider.GetDependency() @@ -202,11 +249,7 @@ public class DeviceServiceTests sutProvider.GetDependency() .GetManyByUserIdAsync(currentUserId) - .Returns(new List - { - deviceOne, - deviceTwo, - }); + .Returns(new List { deviceOne, deviceTwo, }); var currentDeviceModel = new DeviceKeysUpdateRequestModel { @@ -236,11 +279,7 @@ public class DeviceServiceTests sutProvider.GetDependency() .GetManyByUserIdAsync(currentUserId) - .Returns(new List - { - deviceOne, - deviceTwo, - }); + .Returns(new List { deviceOne, deviceTwo, }); var currentDeviceModel = new DeviceKeysUpdateRequestModel { @@ -259,6 +298,7 @@ public class DeviceServiceTests }; await Assert.ThrowsAsync(() => - sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, alteredDeviceModels)); + sutProvider.Sut.UpdateDevicesTrustAsync("current_device", currentUserId, currentDeviceModel, + alteredDeviceModels)); } }