diff --git a/test/Api.IntegrationTest/Helpers/LoginHelper.cs b/test/Api.IntegrationTest/Helpers/LoginHelper.cs index d6ce911bd..1f5eb725d 100644 --- a/test/Api.IntegrationTest/Helpers/LoginHelper.cs +++ b/test/Api.IntegrationTest/Helpers/LoginHelper.cs @@ -16,6 +16,12 @@ public class LoginHelper _client = client; } + public async Task LoginAsync(string email) + { + var tokens = await _factory.LoginAsync(email); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); + } + public async Task LoginWithOrganizationApiKeyAsync(Guid organizationId) { var (clientId, apiKey) = await GetOrganizationApiKey(_factory, organizationId); diff --git a/test/Api.IntegrationTest/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs b/test/Api.IntegrationTest/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs new file mode 100644 index 000000000..77734d0b7 --- /dev/null +++ b/test/Api.IntegrationTest/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs @@ -0,0 +1,124 @@ +using System.Net; +using Bit.Api.IntegrationTest.Factories; +using Bit.Api.IntegrationTest.Helpers; +using Bit.Api.KeyManagement.Models.Requests; +using Bit.Core.Auth.Entities; +using Bit.Core.Auth.Enums; +using Bit.Core.Billing.Enums; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Bit.Test.Common.AutoFixture.Attributes; +using Xunit; + +namespace Bit.Api.IntegrationTest.KeyManagement.Controllers; + +public class AccountsKeyManagementControllerTests : IClassFixture, IAsyncLifetime +{ + private static readonly string _mockEncryptedString = + "2.AOs41Hd8OQiCPXjyJKCiDA==|O6OHgt2U2hJGBSNGnimJmg==|iD33s8B69C8JhYYhSa4V1tArjvLr8eEaGqOV7BRo5Jk="; + + private readonly HttpClient _client; + private readonly IEmergencyAccessRepository _emergencyAccessRepository; + private readonly ApiApplicationFactory _factory; + private readonly LoginHelper _loginHelper; + private readonly IUserRepository _userRepository; + private string _ownerEmail = null!; + + public AccountsKeyManagementControllerTests(ApiApplicationFactory factory) + { + _factory = factory; + _client = factory.CreateClient(); + _loginHelper = new LoginHelper(_factory, _client); + _userRepository = _factory.GetService(); + _emergencyAccessRepository = _factory.GetService(); + } + + public async Task InitializeAsync() + { + _ownerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(_ownerEmail); + } + + public Task DisposeAsync() + { + _client.Dispose(); + return Task.CompletedTask; + } + + [Theory] + [BitAutoData] + public async Task RegenerateKeysAsync_NotLoggedIn_Unauthorized(KeyRegenerationRequestModel request) + { + request.UserKeyEncryptedUserPrivateKey = _mockEncryptedString; + + var response = await _client.PostAsJsonAsync("/accounts/key-management/regenerate-keys", request); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Theory] + [BitAutoData(true, true)] + [BitAutoData(true, false)] + [BitAutoData(false, true)] + public async Task RegenerateKeysAsync_UserInOrgOrHasDesignatedEmergencyAccess_ThrowsBadRequest( + bool inOrganization, + bool hasDesignatedEmergencyAccess, + KeyRegenerationRequestModel request) + { + if (inOrganization) + { + await OrganizationTestHelpers.SignUpAsync(_factory, + PlanType.EnterpriseAnnually, _ownerEmail, passwordManagerSeats: 10, + paymentMethod: PaymentMethodType.Card); + } + + if (hasDesignatedEmergencyAccess) + { + await CreateDesignatedEmergencyAccessAsync(); + } + + await _loginHelper.LoginAsync(_ownerEmail); + request.UserKeyEncryptedUserPrivateKey = _mockEncryptedString; + + var response = await _client.PostAsJsonAsync("/accounts/key-management/regenerate-keys", request); + + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Theory] + [BitAutoData] + public async Task RegenerateKeysAsync_Success(KeyRegenerationRequestModel request) + { + await _loginHelper.LoginAsync(_ownerEmail); + request.UserKeyEncryptedUserPrivateKey = _mockEncryptedString; + + var response = await _client.PostAsJsonAsync("/accounts/key-management/regenerate-keys", request); + response.EnsureSuccessStatusCode(); + + var user = await _userRepository.GetByEmailAsync(_ownerEmail); + Assert.NotNull(user); + Assert.Equal(request.UserPublicKey, user.PublicKey); + Assert.Equal(request.UserKeyEncryptedUserPrivateKey, user.PrivateKey); + } + + private async Task CreateDesignatedEmergencyAccessAsync() + { + var tempEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; + await _factory.LoginWithNewAccount(tempEmail); + + var tempUser = await _userRepository.GetByEmailAsync(tempEmail); + var user = await _userRepository.GetByEmailAsync(_ownerEmail); + var emergencyAccess = new EmergencyAccess + { + GrantorId = tempUser!.Id, + GranteeId = user!.Id, + KeyEncrypted = _mockEncryptedString, + Status = EmergencyAccessStatusType.Confirmed, + Type = EmergencyAccessType.View, + WaitTimeDays = 10, + CreationDate = DateTime.UtcNow, + RevisionDate = DateTime.UtcNow + }; + await _emergencyAccessRepository.CreateAsync(emergencyAccess); + } +} diff --git a/test/Api.Test/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs b/test/Api.Test/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs new file mode 100644 index 000000000..bc0a6c64d --- /dev/null +++ b/test/Api.Test/KeyManagement/Controllers/AccountsKeyManagementControllerTests.cs @@ -0,0 +1,61 @@ +#nullable enable +using System.Security.Claims; +using Bit.Api.KeyManagement.Controllers; +using Bit.Api.KeyManagement.Models.Requests; +using Bit.Core.Auth.Models.Data; +using Bit.Core.Entities; +using Bit.Core.KeyManagement.Commands.Interfaces; +using Bit.Core.KeyManagement.Models.Data; +using Bit.Core.Repositories; +using Bit.Core.Services; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using NSubstitute; +using NSubstitute.ReturnsExtensions; +using Xunit; + +namespace Bit.Api.Test.KeyManagement.Controllers; + +[ControllerCustomize(typeof(AccountsKeyManagementController))] +[SutProviderCustomize] +[JsonDocumentCustomize] +public class AccountsKeyManagementControllerTests +{ + [Theory] + [BitAutoData] + public async Task RegenerateKeysAsync_UserNull_Throws(SutProvider sutProvider, + KeyRegenerationRequestModel data) + { + sutProvider.GetDependency().GetUserByPrincipalAsync(Arg.Any()).ReturnsNull(); + + await Assert.ThrowsAsync(() => sutProvider.Sut.RegenerateKeysAsync(data)); + + await sutProvider.GetDependency().ReceivedWithAnyArgs(0) + .GetManyByUserAsync(Arg.Any()); + await sutProvider.GetDependency().ReceivedWithAnyArgs(0) + .GetManyDetailsByGranteeIdAsync(Arg.Any()); + await sutProvider.GetDependency().ReceivedWithAnyArgs(0) + .RegenerateKeysAsync(Arg.Any(), + Arg.Any>(), + Arg.Any>()); + } + + [Theory] + [BitAutoData] + public async Task RegenerateKeysAsync_Success(SutProvider sutProvider, + KeyRegenerationRequestModel data, User user) + { + sutProvider.GetDependency().GetUserByPrincipalAsync(Arg.Any()).Returns(user); + + await sutProvider.Sut.RegenerateKeysAsync(data); + + await sutProvider.GetDependency().Received(1) + .GetManyByUserAsync(Arg.Is(user.Id)); + await sutProvider.GetDependency().Received(1) + .GetManyDetailsByGranteeIdAsync(Arg.Is(user.Id)); + await sutProvider.GetDependency().ReceivedWithAnyArgs(1) + .RegenerateKeysAsync(Arg.Any(), + Arg.Any>(), + Arg.Any>()); + } +}