1
0
mirror of https://github.com/bitwarden/server synced 2026-01-28 15:23:38 +00:00

test(register): [PM-27084] Account Register Uses New Data Types - Test additions to make sure properties that were once required are still required.

This commit is contained in:
Patrick Pimentel
2025-12-16 10:38:15 -05:00
parent b33e1aced2
commit c21d7b43df
2 changed files with 226 additions and 3 deletions

View File

@@ -821,6 +821,225 @@ public class AccountsControllerTests : IDisposable
organizationUserId);
}
[Theory, BitAutoData]
public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRootFieldsNull(
string email,
string emailVerificationToken,
string masterPasswordHash,
string masterKeyWrappedUserKey,
int iterations,
string publicKey,
string encryptedPrivateKey)
{
// Arrange: Provide only unlock-data KDF + key; leave root KDF fields null
var unlockKdf = new KdfSettings
{
KdfType = KdfType.PBKDF2_SHA256,
Iterations = iterations
};
var model = new RegisterFinishRequestModel
{
Email = email,
EmailVerificationToken = emailVerificationToken,
MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData
{
// present but not used by ToUser for KDF/Key
Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations },
MasterPasswordAuthenticationHash = masterPasswordHash,
Salt = email
},
MasterPasswordUnlockData = new MasterPasswordUnlockData
{
Kdf = unlockKdf,
MasterKeyWrappedUserKey = masterKeyWrappedUserKey,
Salt = email
},
// root KDF fields intentionally null
Kdf = null,
KdfIterations = null,
UserAsymmetricKeys = new KeysRequestModel
{
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
}
};
_registerUserCommand
.RegisterUserViaEmailVerificationToken(Arg.Any<User>(), masterPasswordHash, emailVerificationToken)
.Returns(Task.FromResult(IdentityResult.Success));
// Act
var _ = await _sut.PostRegisterFinish(model);
// Assert: The user passed to command uses unlock-data values
await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken(
Arg.Is<User>(u =>
u.Email == email &&
u.Kdf == unlockKdf.KdfType &&
u.KdfIterations == unlockKdf.Iterations &&
u.Key == masterKeyWrappedUserKey),
masterPasswordHash,
emailVerificationToken);
}
[Theory, BitAutoData]
public async Task PostRegisterFinish_LegacyForm_UsesRootFields_WhenUnlockDataNull(
string email,
string emailVerificationToken,
string masterPasswordHash,
string legacyKey,
string publicKey,
string encryptedPrivateKey)
{
// Arrange: Provide only legacy root KDF + key; no unlock-data provided
var model = new RegisterFinishRequestModel
{
Email = email,
EmailVerificationToken = emailVerificationToken,
MasterPasswordHash = masterPasswordHash,
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
UserSymmetricKey = legacyKey,
MasterPasswordUnlockData = null,
UserAsymmetricKeys = new KeysRequestModel
{
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
}
};
_registerUserCommand
.RegisterUserViaEmailVerificationToken(Arg.Any<User>(), masterPasswordHash, emailVerificationToken)
.Returns(Task.FromResult(IdentityResult.Success));
// Act
var _ = await _sut.PostRegisterFinish(model);
// Assert: The user passed to command uses root values
await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken(
Arg.Is<User>(u =>
u.Email == email &&
u.Kdf == KdfType.PBKDF2_SHA256 &&
u.KdfIterations == AuthConstants.PBKDF2_ITERATIONS.Default &&
u.Key == legacyKey),
masterPasswordHash,
emailVerificationToken);
}
[Theory, BitAutoData]
public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadRequest(
string email,
string emailVerificationToken,
string masterPasswordHash,
string masterKeyWrappedUserKey,
int iterations,
string publicKey,
string encryptedPrivateKey)
{
// Arrange: No KDF at root, and no unlock-data present
var model = new RegisterFinishRequestModel
{
Email = email,
EmailVerificationToken = emailVerificationToken,
MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData
{
// present but ToUser does not source KDF from here
Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations },
MasterPasswordAuthenticationHash = masterPasswordHash,
Salt = email
},
MasterPasswordUnlockData = null,
Kdf = null,
KdfIterations = iterations,
UserSymmetricKey = masterKeyWrappedUserKey,
UserAsymmetricKeys = new KeysRequestModel
{
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
}
};
// Act & Assert
var ex = await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegisterFinish(model));
Assert.Equal("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in.", ex.Message);
}
[Theory, BitAutoData]
public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_ShouldReturnBadRequest(
string email,
string emailVerificationToken,
string masterPasswordHash,
string masterKeyWrappedUserKey,
KdfType kdfType,
string publicKey,
string encryptedPrivateKey)
{
// Arrange: No KdfIterations at root, and no unlock-data present
var model = new RegisterFinishRequestModel
{
Email = email,
EmailVerificationToken = emailVerificationToken,
MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData
{
// present but ToUser does not source iterations from here
Kdf = new KdfSettings { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default },
MasterPasswordAuthenticationHash = masterPasswordHash,
Salt = email
},
MasterPasswordUnlockData = null,
Kdf = kdfType,
KdfIterations = null,
UserSymmetricKey = masterKeyWrappedUserKey,
UserAsymmetricKeys = new KeysRequestModel
{
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
}
};
// Act & Assert
var ex = await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegisterFinish(model));
Assert.Equal("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in.", ex.Message);
}
[Theory, BitAutoData]
public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadRequest(
string email,
string emailVerificationToken,
string masterPasswordHash,
int iterations,
KdfType kdfType,
string publicKey,
string encryptedPrivateKey)
{
// Arrange: No key at root, and no unlock-data present
var model = new RegisterFinishRequestModel
{
Email = email,
EmailVerificationToken = emailVerificationToken,
MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData
{
Kdf = new KdfSettings { KdfType = kdfType, Iterations = iterations },
MasterPasswordAuthenticationHash = masterPasswordHash,
Salt = email
},
MasterPasswordUnlockData = null,
Kdf = kdfType,
KdfIterations = iterations,
UserSymmetricKey = null,
UserAsymmetricKeys = new KeysRequestModel
{
PublicKey = publicKey,
EncryptedPrivateKey = encryptedPrivateKey
}
};
// Act & Assert
var ex = await Assert.ThrowsAsync<BadRequestException>(() => _sut.PostRegisterFinish(model));
Assert.Equal("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in.", ex.Message);
}
private void SetDefaultKdfHmacKey(byte[]? newKey)
{
var fieldInfo = typeof(AccountsController).GetField("_defaultKdfHmacKey", BindingFlags.NonPublic | BindingFlags.Instance);