mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
Improve tests
This commit is contained in:
@ -12,6 +12,9 @@ using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Entities;
|
||||
using Bit.Core.KeyManagement.Enums;
|
||||
using Bit.Core.KeyManagement.Repositories;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Vault.Enums;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
@ -24,6 +27,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
{
|
||||
private static readonly string _mockEncryptedString =
|
||||
"2.AOs41Hd8OQiCPXjyJKCiDA==|O6OHgt2U2hJGBSNGnimJmg==|iD33s8B69C8JhYYhSa4V1tArjvLr8eEaGqOV7BRo5Jk=";
|
||||
private static readonly string _mockEncryptedType7String = "7.AOs41Hd8OQiCPXjyJKCiDA==";
|
||||
|
||||
private readonly HttpClient _client;
|
||||
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
|
||||
@ -34,6 +38,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IPasswordHasher<User> _passwordHasher;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IUserSignatureKeyPairRepository _userSignatureKeyPairRepository;
|
||||
private string _ownerEmail = null!;
|
||||
|
||||
public AccountsKeyManagementControllerTests(ApiApplicationFactory factory)
|
||||
@ -49,6 +54,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
_organizationUserRepository = _factory.GetService<IOrganizationUserRepository>();
|
||||
_passwordHasher = _factory.GetService<IPasswordHasher<User>>();
|
||||
_organizationRepository = _factory.GetService<IOrganizationRepository>();
|
||||
_userSignatureKeyPairRepository = _factory.GetService<IUserSignatureKeyPairRepository>();
|
||||
}
|
||||
|
||||
public async Task InitializeAsync()
|
||||
@ -200,6 +206,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
var password = _passwordHasher.HashPassword(user, "newMasterPassword");
|
||||
user.MasterPassword = password;
|
||||
user.PublicKey = "publicKey";
|
||||
user.PrivateKey = _mockEncryptedString;
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfType = user.Kdf;
|
||||
@ -209,6 +216,8 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.Email = user.Email;
|
||||
request.AccountKeys.AccountPublicKey = "publicKey";
|
||||
request.AccountKeys.UserKeyEncryptedAccountPrivateKey = _mockEncryptedString;
|
||||
request.AccountKeys.PublicKeyEncryptionKeyPair = null;
|
||||
request.AccountKeys.SignatureKeyPair = null;
|
||||
|
||||
request.OldMasterKeyAuthenticationHash = "newMasterPassword";
|
||||
|
||||
@ -354,4 +363,191 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
|
||||
Assert.Equal(DateTime.UtcNow, user.RevisionDate, TimeSpan.FromMinutes(1));
|
||||
Assert.Equal(DateTime.UtcNow, user.AccountRevisionDate, TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RotateV2UserAccountKeysAsync_Success(RotateUserAccountKeysAndDataRequestModel request)
|
||||
{
|
||||
await _loginHelper.LoginAsync(_ownerEmail);
|
||||
var user = await _userRepository.GetByEmailAsync(_ownerEmail);
|
||||
if (user == null)
|
||||
{
|
||||
throw new InvalidOperationException("User not found.");
|
||||
}
|
||||
|
||||
var password = _passwordHasher.HashPassword(user, "newMasterPassword");
|
||||
user.MasterPassword = password;
|
||||
user.PublicKey = "publicKey";
|
||||
user.PrivateKey = _mockEncryptedType7String;
|
||||
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
await _userSignatureKeyPairRepository.CreateAsync(new UserSignatureKeyPair
|
||||
{
|
||||
UserId = user.Id,
|
||||
SignatureAlgorithm = SignatureAlgorithm.Ed25519,
|
||||
SigningKey = _mockEncryptedType7String,
|
||||
VerifyingKey = "verifyingKey",
|
||||
});
|
||||
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfType = user.Kdf;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfIterations = user.KdfIterations;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfMemory = user.KdfMemory;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfParallelism = user.KdfParallelism;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.Email = user.Email;
|
||||
request.AccountKeys.AccountPublicKey = "publicKey";
|
||||
request.AccountKeys.UserKeyEncryptedAccountPrivateKey = _mockEncryptedType7String;
|
||||
request.AccountKeys.PublicKeyEncryptionKeyPair = new PublicKeyEncryptionKeyPairRequestModel
|
||||
{
|
||||
PublicKey = "publicKey",
|
||||
WrappedPrivateKey = _mockEncryptedType7String,
|
||||
SignedPublicKey = "signedPublicKey",
|
||||
};
|
||||
request.AccountKeys.SignatureKeyPair = new SignatureKeyPairRequestModel
|
||||
{
|
||||
SignatureAlgorithm = SignatureAlgorithm.Ed25519,
|
||||
WrappedSigningKey = _mockEncryptedType7String,
|
||||
VerifyingKey = "verifyingKey",
|
||||
};
|
||||
|
||||
request.OldMasterKeyAuthenticationHash = "newMasterPassword";
|
||||
|
||||
request.AccountData.Ciphers =
|
||||
[
|
||||
new CipherWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Type = CipherType.Login,
|
||||
Name = _mockEncryptedString,
|
||||
Login = new CipherLoginModel
|
||||
{
|
||||
Username = _mockEncryptedString,
|
||||
Password = _mockEncryptedString,
|
||||
},
|
||||
},
|
||||
];
|
||||
request.AccountData.Folders = [
|
||||
new FolderWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = _mockEncryptedString,
|
||||
},
|
||||
];
|
||||
request.AccountData.Sends = [
|
||||
new SendWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = _mockEncryptedString,
|
||||
Key = _mockEncryptedString,
|
||||
Disabled = false,
|
||||
DeletionDate = DateTime.UtcNow.AddDays(1),
|
||||
},
|
||||
];
|
||||
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);
|
||||
Assert.NotNull(userNewState);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.Email, userNewState.Email);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfType, userNewState.Kdf);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfIterations, userNewState.KdfIterations);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfMemory, userNewState.KdfMemory);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfParallelism, userNewState.KdfParallelism);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RotateUpgradeToV2UserAccountKeysAsync_Success(RotateUserAccountKeysAndDataRequestModel request)
|
||||
{
|
||||
await _loginHelper.LoginAsync(_ownerEmail);
|
||||
var user = await _userRepository.GetByEmailAsync(_ownerEmail);
|
||||
if (user == null)
|
||||
{
|
||||
throw new InvalidOperationException("User not found.");
|
||||
}
|
||||
|
||||
var password = _passwordHasher.HashPassword(user, "newMasterPassword");
|
||||
user.MasterPassword = password;
|
||||
user.PublicKey = "publicKey";
|
||||
user.PrivateKey = _mockEncryptedString;
|
||||
|
||||
await _userRepository.ReplaceAsync(user);
|
||||
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfType = user.Kdf;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfIterations = user.KdfIterations;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfMemory = user.KdfMemory;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.KdfParallelism = user.KdfParallelism;
|
||||
request.AccountUnlockData.MasterPasswordUnlockData.Email = user.Email;
|
||||
request.AccountKeys.AccountPublicKey = "publicKey";
|
||||
request.AccountKeys.UserKeyEncryptedAccountPrivateKey = _mockEncryptedType7String;
|
||||
request.AccountKeys.PublicKeyEncryptionKeyPair = new PublicKeyEncryptionKeyPairRequestModel
|
||||
{
|
||||
PublicKey = "publicKey",
|
||||
WrappedPrivateKey = _mockEncryptedType7String,
|
||||
SignedPublicKey = "signedPublicKey",
|
||||
};
|
||||
request.AccountKeys.SignatureKeyPair = new SignatureKeyPairRequestModel
|
||||
{
|
||||
SignatureAlgorithm = SignatureAlgorithm.Ed25519,
|
||||
WrappedSigningKey = _mockEncryptedType7String,
|
||||
VerifyingKey = "verifyingKey",
|
||||
};
|
||||
|
||||
request.OldMasterKeyAuthenticationHash = "newMasterPassword";
|
||||
|
||||
request.AccountData.Ciphers =
|
||||
[
|
||||
new CipherWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Type = CipherType.Login,
|
||||
Name = _mockEncryptedString,
|
||||
Login = new CipherLoginModel
|
||||
{
|
||||
Username = _mockEncryptedString,
|
||||
Password = _mockEncryptedString,
|
||||
},
|
||||
},
|
||||
];
|
||||
request.AccountData.Folders = [
|
||||
new FolderWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = _mockEncryptedString,
|
||||
},
|
||||
];
|
||||
request.AccountData.Sends = [
|
||||
new SendWithIdRequestModel
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = _mockEncryptedString,
|
||||
Key = _mockEncryptedString,
|
||||
Disabled = false,
|
||||
DeletionDate = DateTime.UtcNow.AddDays(1),
|
||||
},
|
||||
];
|
||||
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);
|
||||
Assert.NotNull(userNewState);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.Email, userNewState.Email);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfType, userNewState.Kdf);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfIterations, userNewState.KdfIterations);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfMemory, userNewState.KdfMemory);
|
||||
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfParallelism, userNewState.KdfParallelism);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Repositories;
|
||||
using Bit.Core.KeyManagement.UserKey.Implementations;
|
||||
@ -7,6 +8,7 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using NSubstitute;
|
||||
using NSubstitute.ReturnsExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.KeyManagement.UserKey;
|
||||
@ -194,4 +196,75 @@ public class RotateUserAccountKeysCommandTests
|
||||
Assert.Equal(IdentityResult.Success, result);
|
||||
}
|
||||
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateAccountKeys_ThrowsIfPublicKeyChangesAsync(SutProvider<RotateUserAccountKeysCommand> sutProvider, User user, RotateUserAccountKeysData model)
|
||||
{
|
||||
user.PrivateKey = "2.xxx";
|
||||
sutProvider.GetDependency<IUserSignatureKeyPairRepository>()
|
||||
.GetByUserIdAsync(user.Id)
|
||||
.ReturnsNull();
|
||||
user.PublicKey = "old-public";
|
||||
model.AccountKeys.PublicKeyEncryptionKeyPairData.PublicKey = "new-public";
|
||||
var saveEncryptedDataActions = new List<Core.KeyManagement.UserKey.UpdateEncryptedDataForKeyRotation>();
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await sutProvider.Sut.UpdateAccountKeys(model, user, saveEncryptedDataActions));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateAccountKeys_ThrowsIfPrivateKeyWrappedWithNotXchacha20ForV2UserAsync(SutProvider<RotateUserAccountKeysCommand> sutProvider, User user, RotateUserAccountKeysData model)
|
||||
{
|
||||
user.PrivateKey = "7.xxx";
|
||||
sutProvider.GetDependency<IUserSignatureKeyPairRepository>()
|
||||
.GetByUserIdAsync(user.Id)
|
||||
.Returns(new SignatureKeyPairData(SignatureAlgorithm.Ed25519, "7.xxx", "public"));
|
||||
user.PublicKey = "public";
|
||||
model.UserKeyEncryptedAccountPrivateKey = "2.xxx";
|
||||
model.AccountPublicKey = user.PublicKey;
|
||||
model.AccountKeys.PublicKeyEncryptionKeyPairData = new PublicKeyEncryptionKeyPairData("2.xxx", "public", null);
|
||||
model.AccountKeys.SignatureKeyPairData = null;
|
||||
var saveEncryptedDataActions = new List<Core.KeyManagement.UserKey.UpdateEncryptedDataForKeyRotation>();
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await sutProvider.Sut.UpdateAccountKeys(model, user, saveEncryptedDataActions));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateAccountKeys_ThrowsIfIsV1UserAndPrivateKeyIsWrappedWithNotAesCbcHmacAsync(SutProvider<RotateUserAccountKeysCommand> sutProvider, User user, RotateUserAccountKeysData model)
|
||||
{
|
||||
user.PrivateKey = "2.xxx";
|
||||
sutProvider.GetDependency<IUserSignatureKeyPairRepository>()
|
||||
.GetByUserIdAsync(user.Id)
|
||||
.ReturnsNull();
|
||||
user.PublicKey = "public";
|
||||
model.UserKeyEncryptedAccountPrivateKey = "7.xxx";
|
||||
model.AccountPublicKey = "public";
|
||||
model.AccountKeys.PublicKeyEncryptionKeyPairData = null;
|
||||
model.AccountKeys.SignatureKeyPairData = null;
|
||||
var saveEncryptedDataActions = new List<Core.KeyManagement.UserKey.UpdateEncryptedDataForKeyRotation>();
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(async () => await sutProvider.Sut.UpdateAccountKeys(model, user, saveEncryptedDataActions));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task UpdateAccountKeys_SuccessAsync(SutProvider<RotateUserAccountKeysCommand> sutProvider, User user, RotateUserAccountKeysData model)
|
||||
{
|
||||
user.PrivateKey = "2.xxx";
|
||||
sutProvider.GetDependency<IUserSignatureKeyPairRepository>()
|
||||
.GetByUserIdAsync(user.Id)
|
||||
.Returns(new SignatureKeyPairData(SignatureAlgorithm.Ed25519, "7.xxx", "public"));
|
||||
user.PublicKey = "public";
|
||||
model.AccountKeys.PublicKeyEncryptionKeyPairData.PublicKey = "public";
|
||||
model.UserKeyEncryptedAccountPrivateKey = "2.xxx";
|
||||
var saveEncryptedDataActions = new List<Core.KeyManagement.UserKey.UpdateEncryptedDataForKeyRotation>();
|
||||
await sutProvider.Sut.UpdateAccountKeys(model, user, saveEncryptedDataActions);
|
||||
Assert.NotEmpty(saveEncryptedDataActions);
|
||||
}
|
||||
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void ValidateRotationModelSignatureKeyPairForv1UserAndUpgradeToV2_NoSignedPublicKeyThrows(SutProvider<RotateUserAccountKeysCommand> sutProvider, User user, RotateUserAccountKeysData model)
|
||||
{
|
||||
model.AccountKeys.SignatureKeyPairData = new SignatureKeyPairData(SignatureAlgorithm.Ed25519, "signingKey", "verifyingKey");
|
||||
model.AccountKeys.PublicKeyEncryptionKeyPairData.SignedPublicKey = null;
|
||||
var encryptedDataActions = new List<Core.KeyManagement.UserKey.UpdateEncryptedDataForKeyRotation>();
|
||||
var excepction = Assert.Throws<InvalidOperationException>(() => sutProvider.Sut.ValidateRotationModelSignatureKeyPairForV1UserAndUpgradeToV2(model, user, encryptedDataActions));
|
||||
Assert.Equal("The provided public key encryption key pair data does not contain a valid signed public key.", excepction.Message);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user