mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[PM-2199] Implement userkey rotation for all TDE devices (#5446)
* Implement userkey rotation v2 * Update request models * Cleanup * Update tests * Improve test * Add tests * Fix formatting * Fix test * Remove whitespace * Fix namespace * Enable nullable on models * Fix build * Add tests and enable nullable on masterpasswordunlockdatamodel * Fix test * Remove rollback * Add tests * Make masterpassword hint optional * Update user query * Add EF test * Improve test * Cleanup * Set masterpassword hint * Remove connection close * Add tests for invalid kdf types * Update test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Fix formatting * Update src/Api/KeyManagement/Models/Requests/RotateAccountKeysAndDataRequestModel.cs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update src/Api/Auth/Models/Request/Accounts/MasterPasswordUnlockDataModel.cs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update src/Api/Auth/Models/Request/Accounts/MasterPasswordUnlockDataModel.cs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Update src/Api/KeyManagement/Models/Requests/AccountKeysRequestModel.cs Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> * Fix imports * Fix tests * Add poc for tde rotation * Improve rotation transaction safety * Add validator tests * Clean up validator * Add newline * Add devicekey unlock data to integration test * Fix tests * Fix tests * Remove null check * Remove null check * Fix IsTrusted returning wrong result * Add rollback * Cleanup * Address feedback * Further renames --------- Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
This commit is contained in:
@ -29,6 +29,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
private readonly ApiApplicationFactory _factory;
|
||||
private readonly LoginHelper _loginHelper;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IPasswordHasher<User> _passwordHasher;
|
||||
private string _ownerEmail = null!;
|
||||
|
||||
@ -40,6 +41,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
_client = factory.CreateClient();
|
||||
_loginHelper = new LoginHelper(_factory, _client);
|
||||
_userRepository = _factory.GetService<IUserRepository>();
|
||||
_deviceRepository = _factory.GetService<IDeviceRepository>();
|
||||
_emergencyAccessRepository = _factory.GetService<IEmergencyAccessRepository>();
|
||||
_organizationUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
_passwordHasher = _factory.GetService<IPasswordHasher<User>>();
|
||||
@ -238,10 +240,12 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
];
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.MasterKeyEncryptedUserKey = _mockEncryptedString;
|
||||
request.AccountUnlockData.PasskeyUnlockData = [];
|
||||
request.AccountUnlockData.DeviceKeyUnlockData = [];
|
||||
request.AccountUnlockData.EmergencyAccessUnlockData = [];
|
||||
request.AccountUnlockData.OrganizationAccountRecoveryUnlockData = [];
|
||||
|
||||
var response = await _client.PostAsJsonAsync("/accounts/key-management/rotate-user-account-keys", request);
|
||||
var responseMessage = await response.Content.ReadAsStringAsync();
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var userNewState = await _userRepository.GetByEmailAsync(_ownerEmail);
|
||||
|
@ -0,0 +1,49 @@
|
||||
using Bit.Api.KeyManagement.Validators;
|
||||
using Bit.Core.Auth.Models.Api.Request;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Api.Test.KeyManagement.Validators;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class DeviceRotationValidatorTests
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateAsync_SentDevicesAreEmptyButDatabaseDevicesAreNot_Throws(
|
||||
SutProvider<DeviceRotationValidator> sutProvider, User user, IEnumerable<OtherDeviceKeysUpdateRequestModel> devices)
|
||||
{
|
||||
var userCiphers = devices.Select(c => new Device { Id = c.DeviceId, EncryptedPrivateKey = "EncryptedPrivateKey", EncryptedPublicKey = "EncryptedPublicKey", EncryptedUserKey = "EncryptedUserKey" }).ToList();
|
||||
sutProvider.GetDependency<IDeviceRepository>().GetManyByUserIdAsync(user.Id)
|
||||
.Returns(userCiphers);
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.ValidateAsync(user, Enumerable.Empty<OtherDeviceKeysUpdateRequestModel>()));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateAsync_SentDevicesTrustedButDatabaseUntrusted_Throws(
|
||||
SutProvider<DeviceRotationValidator> sutProvider, User user, IEnumerable<OtherDeviceKeysUpdateRequestModel> devices)
|
||||
{
|
||||
var userCiphers = devices.Select(c => new Device { Id = c.DeviceId, EncryptedPrivateKey = "Key", EncryptedPublicKey = "Key", EncryptedUserKey = "Key" }).ToList();
|
||||
sutProvider.GetDependency<IDeviceRepository>().GetManyByUserIdAsync(user.Id)
|
||||
.Returns(userCiphers);
|
||||
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.ValidateAsync(user, [
|
||||
new OtherDeviceKeysUpdateRequestModel { DeviceId = userCiphers.First().Id, EncryptedPublicKey = null, EncryptedUserKey = null }
|
||||
]));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task ValidateAsync_Validates(
|
||||
SutProvider<DeviceRotationValidator> sutProvider, User user, IEnumerable<OtherDeviceKeysUpdateRequestModel> devices)
|
||||
{
|
||||
var userCiphers = devices.Select(c => new Device { Id = c.DeviceId, EncryptedPrivateKey = "Key", EncryptedPublicKey = "Key", EncryptedUserKey = "Key" }).ToList().Slice(0, 1);
|
||||
sutProvider.GetDependency<IDeviceRepository>().GetManyByUserIdAsync(user.Id)
|
||||
.Returns(userCiphers);
|
||||
Assert.NotEmpty(await sutProvider.Sut.ValidateAsync(user, [
|
||||
new OtherDeviceKeysUpdateRequestModel { DeviceId = userCiphers.First().Id, EncryptedPublicKey = "Key", EncryptedUserKey = "Key" }
|
||||
]));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user