#nullable enable using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Data; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Commands; using Bit.Core.KeyManagement.Models.Data; using Bit.Core.KeyManagement.Repositories; using Bit.Core.Platform.Push; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using NSubstitute.ReturnsExtensions; using Xunit; namespace Bit.Core.Test.KeyManagement.Commands; [SutProviderCustomize] public class RegenerateUserAsymmetricKeysCommandTests { [Theory] [BitAutoData] public async Task RegenerateKeysAsync_NoCurrentContext_NotFoundException( SutProvider<RegenerateUserAsymmetricKeysCommand> sutProvider, UserAsymmetricKeys userAsymmetricKeys) { sutProvider.GetDependency<ICurrentContext>().UserId.ReturnsNullForAnyArgs(); var usersOrganizationAccounts = new List<OrganizationUser>(); var designatedEmergencyAccess = new List<EmergencyAccessDetails>(); await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.RegenerateKeysAsync(userAsymmetricKeys, usersOrganizationAccounts, designatedEmergencyAccess)); } [Theory] [BitAutoData] public async Task RegenerateKeysAsync_UserHasNoSharedAccess_Success( SutProvider<RegenerateUserAsymmetricKeysCommand> sutProvider, UserAsymmetricKeys userAsymmetricKeys) { sutProvider.GetDependency<ICurrentContext>().UserId.ReturnsForAnyArgs(userAsymmetricKeys.UserId); var usersOrganizationAccounts = new List<OrganizationUser>(); var designatedEmergencyAccess = new List<EmergencyAccessDetails>(); await sutProvider.Sut.RegenerateKeysAsync(userAsymmetricKeys, usersOrganizationAccounts, designatedEmergencyAccess); await sutProvider.GetDependency<IUserAsymmetricKeysRepository>() .Received(1) .RegenerateUserAsymmetricKeysAsync(Arg.Is(userAsymmetricKeys)); await sutProvider.GetDependency<IPushNotificationService>() .Received(1) .PushSyncSettingsAsync(Arg.Is(userAsymmetricKeys.UserId)); } [Theory] [BitAutoData(false, false, true)] [BitAutoData(false, true, false)] [BitAutoData(false, true, true)] [BitAutoData(true, false, false)] [BitAutoData(true, false, true)] [BitAutoData(true, true, false)] [BitAutoData(true, true, true)] public async Task RegenerateKeysAsync_UserIdMisMatch_NotFoundException( bool userAsymmetricKeysMismatch, bool orgMismatch, bool emergencyAccessMismatch, SutProvider<RegenerateUserAsymmetricKeysCommand> sutProvider, UserAsymmetricKeys userAsymmetricKeys, ICollection<OrganizationUser> usersOrganizationAccounts, ICollection<EmergencyAccessDetails> designatedEmergencyAccess) { sutProvider.GetDependency<ICurrentContext>().UserId .ReturnsForAnyArgs(userAsymmetricKeysMismatch ? new Guid() : userAsymmetricKeys.UserId); if (!orgMismatch) { usersOrganizationAccounts = SetupOrganizationUserAccounts(userAsymmetricKeys.UserId, usersOrganizationAccounts); } if (!emergencyAccessMismatch) { designatedEmergencyAccess = SetupEmergencyAccess(userAsymmetricKeys.UserId, designatedEmergencyAccess); } await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.RegenerateKeysAsync(userAsymmetricKeys, usersOrganizationAccounts, designatedEmergencyAccess)); await sutProvider.GetDependency<IUserAsymmetricKeysRepository>() .ReceivedWithAnyArgs(0) .RegenerateUserAsymmetricKeysAsync(Arg.Any<UserAsymmetricKeys>()); await sutProvider.GetDependency<IPushNotificationService>() .ReceivedWithAnyArgs(0) .PushSyncSettingsAsync(Arg.Any<Guid>()); } [Theory] [BitAutoData(OrganizationUserStatusType.Confirmed)] [BitAutoData(OrganizationUserStatusType.Revoked)] public async Task RegenerateKeysAsync_UserInOrganizations_BadRequestException( OrganizationUserStatusType organizationUserStatus, SutProvider<RegenerateUserAsymmetricKeysCommand> sutProvider, UserAsymmetricKeys userAsymmetricKeys, ICollection<OrganizationUser> usersOrganizationAccounts) { sutProvider.GetDependency<ICurrentContext>().UserId.ReturnsForAnyArgs(userAsymmetricKeys.UserId); usersOrganizationAccounts = CreateInOrganizationAccounts(userAsymmetricKeys.UserId, organizationUserStatus, usersOrganizationAccounts); var designatedEmergencyAccess = new List<EmergencyAccessDetails>(); await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.RegenerateKeysAsync(userAsymmetricKeys, usersOrganizationAccounts, designatedEmergencyAccess)); await sutProvider.GetDependency<IUserAsymmetricKeysRepository>() .ReceivedWithAnyArgs(0) .RegenerateUserAsymmetricKeysAsync(Arg.Any<UserAsymmetricKeys>()); await sutProvider.GetDependency<IPushNotificationService>() .ReceivedWithAnyArgs(0) .PushSyncSettingsAsync(Arg.Any<Guid>()); } [Theory] [BitAutoData(EmergencyAccessStatusType.Confirmed)] [BitAutoData(EmergencyAccessStatusType.RecoveryApproved)] [BitAutoData(EmergencyAccessStatusType.RecoveryInitiated)] public async Task RegenerateKeysAsync_UserHasDesignatedEmergencyAccess_BadRequestException( EmergencyAccessStatusType statusType, SutProvider<RegenerateUserAsymmetricKeysCommand> sutProvider, UserAsymmetricKeys userAsymmetricKeys, ICollection<EmergencyAccessDetails> designatedEmergencyAccess) { sutProvider.GetDependency<ICurrentContext>().UserId.ReturnsForAnyArgs(userAsymmetricKeys.UserId); designatedEmergencyAccess = CreateDesignatedEmergencyAccess(userAsymmetricKeys.UserId, statusType, designatedEmergencyAccess); var usersOrganizationAccounts = new List<OrganizationUser>(); await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.RegenerateKeysAsync(userAsymmetricKeys, usersOrganizationAccounts, designatedEmergencyAccess)); await sutProvider.GetDependency<IUserAsymmetricKeysRepository>() .ReceivedWithAnyArgs(0) .RegenerateUserAsymmetricKeysAsync(Arg.Any<UserAsymmetricKeys>()); await sutProvider.GetDependency<IPushNotificationService>() .ReceivedWithAnyArgs(0) .PushSyncSettingsAsync(Arg.Any<Guid>()); } private static ICollection<OrganizationUser> CreateInOrganizationAccounts(Guid userId, OrganizationUserStatusType organizationUserStatus, ICollection<OrganizationUser> organizationUserAccounts) { foreach (var organizationUserAccount in organizationUserAccounts) { organizationUserAccount.UserId = userId; organizationUserAccount.Status = organizationUserStatus; } return organizationUserAccounts; } private static ICollection<EmergencyAccessDetails> CreateDesignatedEmergencyAccess(Guid userId, EmergencyAccessStatusType status, ICollection<EmergencyAccessDetails> designatedEmergencyAccess) { foreach (var designated in designatedEmergencyAccess) { designated.GranteeId = userId; designated.Status = status; } return designatedEmergencyAccess; } private static ICollection<OrganizationUser> SetupOrganizationUserAccounts(Guid userId, ICollection<OrganizationUser> organizationUserAccounts) { foreach (var organizationUserAccount in organizationUserAccounts) { organizationUserAccount.UserId = userId; } return organizationUserAccounts; } private static ICollection<EmergencyAccessDetails> SetupEmergencyAccess(Guid userId, ICollection<EmergencyAccessDetails> emergencyAccessDetails) { foreach (var emergencyAccessDetail in emergencyAccessDetails) { emergencyAccessDetail.GranteeId = userId; } return emergencyAccessDetails; } }