diff --git a/src/Core/Services/Implementations/EmergencyAccessService.cs b/src/Core/Services/Implementations/EmergencyAccessService.cs index ca88348afe..a647c07717 100644 --- a/src/Core/Services/Implementations/EmergencyAccessService.cs +++ b/src/Core/Services/Implementations/EmergencyAccessService.cs @@ -62,10 +62,15 @@ namespace Bit.Core.Services public async Task InviteAsync(User invitingUser, string email, EmergencyAccessType type, int waitTime) { - if (! await _userService.CanAccessPremium(invitingUser)) + if (!await _userService.CanAccessPremium(invitingUser)) { throw new BadRequestException("Not a premium user."); } + + if (type == EmergencyAccessType.Takeover && invitingUser.UsesKeyConnector) + { + throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector."); + } var emergencyAccess = new EmergencyAccess { @@ -171,6 +176,11 @@ namespace Bit.Core.Services } var grantor = await _userRepository.GetByIdAsync(confirmingUserId); + if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector) + { + throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector."); + } + var grantee = await _userRepository.GetByIdAsync(emergencyAccess.GranteeId.Value); emergencyAccess.Status = EmergencyAccessStatusType.Confirmed; @@ -188,7 +198,16 @@ namespace Bit.Core.Services { throw new BadRequestException("Emergency Access not valid."); } - + + if (emergencyAccess.Type == EmergencyAccessType.Takeover) + { + var grantor = await _userService.GetUserByIdAsync(emergencyAccess.GrantorId); + if (grantor.UsesKeyConnector) + { + throw new BadRequestException("You cannot use Emergency Access Takeover because you are using Key Connector."); + } + } + await _emergencyAccessRepository.ReplaceAsync(emergencyAccess); } @@ -202,6 +221,13 @@ namespace Bit.Core.Services throw new BadRequestException("Emergency Access not valid."); } + var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId); + + if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector) + { + throw new BadRequestException("You cannot takeover an account that is using Key Connector."); + } + var now = DateTime.UtcNow; emergencyAccess.Status = EmergencyAccessStatusType.RecoveryInitiated; emergencyAccess.RevisionDate = now; @@ -209,8 +235,6 @@ namespace Bit.Core.Services emergencyAccess.LastNotificationDate = now; await _emergencyAccessRepository.ReplaceAsync(emergencyAccess); - var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId); - await _mailService.SendEmergencyAccessRecoveryInitiated(emergencyAccess, NameOrEmail(initiatingUser), grantor.Email); } @@ -277,7 +301,12 @@ namespace Bit.Core.Services } var grantor = await _userRepository.GetByIdAsync(emergencyAccess.GrantorId); - + + if (emergencyAccess.Type == EmergencyAccessType.Takeover && grantor.UsesKeyConnector) + { + throw new BadRequestException("You cannot takeover an account that is using Key Connector."); + } + return (emergencyAccess, grantor); } diff --git a/test/Core.Test/Services/EmergencyAccessServiceTests.cs b/test/Core.Test/Services/EmergencyAccessServiceTests.cs new file mode 100644 index 0000000000..f3812f20f1 --- /dev/null +++ b/test/Core.Test/Services/EmergencyAccessServiceTests.cs @@ -0,0 +1,117 @@ +using Bit.Core.Exceptions; +using Bit.Core.Models.Table; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Core.Test.AutoFixture.Attributes; +using Bit.Core.Test.AutoFixture; +using NSubstitute; +using System.Threading.Tasks; +using System; +using Xunit; + +namespace Bit.Core.Test.Services +{ + public class EmergencyAccessServiceTests + { + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task InviteAsync_UserWithKeyConnectorCannotUseTakeover( + SutProvider sutProvider, User invitingUser, string email, int waitTime) + { + invitingUser.UsesKeyConnector = true; + sutProvider.GetDependency().CanAccessPremium(invitingUser).Returns(true); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.InviteAsync(invitingUser, email, Enums.EmergencyAccessType.Takeover, waitTime)); + + Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task ConfirmUserAsync_UserWithKeyConnectorCannotUseTakeover( + SutProvider sutProvider, User confirmingUser, string key) + { + confirmingUser.UsesKeyConnector = true; + var emergencyAccess = new EmergencyAccess + { + Status = Enums.EmergencyAccessStatusType.Accepted, + GrantorId = confirmingUser.Id, + Type = Enums.EmergencyAccessType.Takeover, + }; + + sutProvider.GetDependency().GetByIdAsync(confirmingUser.Id).Returns(confirmingUser); + sutProvider.GetDependency().GetByIdAsync(Arg.Any()).Returns(emergencyAccess); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.ConfirmUserAsync(new Guid(), key, confirmingUser.Id)); + + Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task SaveAsync_UserWithKeyConnectorCannotUseTakeover( + SutProvider sutProvider, User savingUser) + { + savingUser.UsesKeyConnector = true; + var emergencyAccess = new EmergencyAccess + { + Type = Enums.EmergencyAccessType.Takeover, + GrantorId = savingUser.Id, + }; + + sutProvider.GetDependency().GetUserByIdAsync(savingUser.Id).Returns(savingUser); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.SaveAsync(emergencyAccess, savingUser.Id)); + + Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task InitiateAsync_UserWithKeyConnectorCannotUseTakeover( + SutProvider sutProvider, User initiatingUser, User grantor) + { + grantor.UsesKeyConnector = true; + var emergencyAccess = new EmergencyAccess + { + Status = Enums.EmergencyAccessStatusType.Confirmed, + GranteeId = initiatingUser.Id, + GrantorId = grantor.Id, + Type = Enums.EmergencyAccessType.Takeover, + }; + + sutProvider.GetDependency().GetByIdAsync(Arg.Any()).Returns(emergencyAccess); + sutProvider.GetDependency().GetByIdAsync(grantor.Id).Returns(grantor); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.InitiateAsync(new Guid(), initiatingUser)); + + Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + } + + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task TakeoverAsync_UserWithKeyConnectorCannotUseTakeover( + SutProvider sutProvider, User requestingUser, User grantor) + { + grantor.UsesKeyConnector = true; + var emergencyAccess = new EmergencyAccess + { + GrantorId = grantor.Id, + GranteeId = requestingUser.Id, + Status = Enums.EmergencyAccessStatusType.RecoveryApproved, + Type = Enums.EmergencyAccessType.Takeover, + }; + + sutProvider.GetDependency().GetByIdAsync(Arg.Any()).Returns(emergencyAccess); + sutProvider.GetDependency().GetByIdAsync(grantor.Id).Returns(grantor); + + var exception = await Assert.ThrowsAsync( + () => sutProvider.Sut.TakeoverAsync(new Guid(), requestingUser)); + + Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message); + } + } +}