mirror of
https://github.com/bitwarden/server
synced 2026-01-28 15:23:38 +00:00
fix(register): [PM-27084] Account Register Uses New Data Types - Fixed some tests up.
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Api.KeyManagement.Models.Requests;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.KeyManagement.Models.Api.Request;
|
||||
|
||||
namespace Bit.Api.Auth.Models.Request.Accounts;
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
|
||||
public class MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
public required KdfRequestModel Kdf { get; init; }
|
||||
[EncryptedString] public required string MasterKeyWrappedUserKey { get; init; }
|
||||
[StringLength(256)] public required string Salt { get; init; }
|
||||
|
||||
public MasterPasswordUnlockData ToData()
|
||||
{
|
||||
return new MasterPasswordUnlockData
|
||||
{
|
||||
Kdf = Kdf.ToData(),
|
||||
MasterKeyWrappedUserKey = MasterKeyWrappedUserKey,
|
||||
Salt = Salt
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Models.Api.Request;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
@@ -21,8 +21,8 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
public required string Email { get; set; }
|
||||
public string? EmailVerificationToken { get; set; }
|
||||
|
||||
public MasterPasswordAuthenticationData? MasterPasswordAuthentication { get; set; }
|
||||
public MasterPasswordUnlockData? MasterPasswordUnlock { get; set; }
|
||||
public MasterPasswordAuthenticationDataRequestModel? MasterPasswordAuthentication { get; set; }
|
||||
public MasterPasswordUnlockDataRequestModel? MasterPasswordUnlock { get; set; }
|
||||
|
||||
// PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData)
|
||||
[StringLength(1000)]
|
||||
@@ -66,10 +66,10 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
// PM-28143 - Remove line below
|
||||
// When we process this request to a user object, check if the unlock and authentication
|
||||
// data has been passed through, and if so they should have matching values.
|
||||
MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock);
|
||||
MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock);
|
||||
|
||||
// PM-28143 - Remove line below
|
||||
MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash);
|
||||
MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash);
|
||||
|
||||
var user = new User
|
||||
{
|
||||
@@ -122,10 +122,10 @@ public class RegisterFinishRequestModel : IValidatableObject
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
// PM-28143 - Remove line below
|
||||
MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock);
|
||||
MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock);
|
||||
|
||||
// PM-28143 - Remove line below
|
||||
MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash);
|
||||
MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash);
|
||||
|
||||
// PM-28143 - Remove line below
|
||||
var kdf = MasterPasswordUnlock?.Kdf.KdfType
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
namespace Bit.Core.KeyManagement.Models.Api.Request;
|
||||
|
||||
public class MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
@@ -18,4 +18,17 @@ public class MasterPasswordAuthenticationDataRequestModel
|
||||
Salt = Salt
|
||||
};
|
||||
}
|
||||
|
||||
public static void ThrowIfExistsAndHashIsNotEqual(
|
||||
MasterPasswordAuthenticationDataRequestModel? authenticationData,
|
||||
string? hash)
|
||||
{
|
||||
if (authenticationData != null && hash != null)
|
||||
{
|
||||
if (authenticationData.MasterPasswordAuthenticationHash != hash)
|
||||
{
|
||||
throw new Exception("Master password hash and hash are not equal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.KeyManagement.Models.Api.Request;
|
||||
|
||||
public class MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
public required KdfRequestModel Kdf { get; init; }
|
||||
[EncryptedString] public required string MasterKeyWrappedUserKey { get; init; }
|
||||
[StringLength(256)] public required string Salt { get; init; }
|
||||
|
||||
public MasterPasswordUnlockData ToData()
|
||||
{
|
||||
return new MasterPasswordUnlockData
|
||||
{
|
||||
Kdf = Kdf.ToData(),
|
||||
MasterKeyWrappedUserKey = MasterKeyWrappedUserKey,
|
||||
Salt = Salt
|
||||
};
|
||||
}
|
||||
|
||||
public static void ThrowIfExistsAndNotMatchingAuthenticationData(
|
||||
MasterPasswordAuthenticationDataRequestModel? authenticationData,
|
||||
MasterPasswordUnlockDataRequestModel? unlockData)
|
||||
{
|
||||
if (unlockData != null && authenticationData != null)
|
||||
{
|
||||
var matches = MatchesAuthenticationData(
|
||||
unlockData,
|
||||
authenticationData);
|
||||
|
||||
if (!matches)
|
||||
{
|
||||
throw new Exception("KDF settings and salt must match between authentication and unlock data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchesAuthenticationData(
|
||||
MasterPasswordUnlockDataRequestModel unlockData,
|
||||
MasterPasswordAuthenticationDataRequestModel authenticationData)
|
||||
{
|
||||
var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf);
|
||||
var saltMatches = unlockData.Salt == authenticationData.Salt;
|
||||
|
||||
return kdfMatches && saltMatches;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Requests;
|
||||
namespace Bit.Core.KeyManagement.Models.Data;
|
||||
|
||||
public class KdfRequestModel
|
||||
{
|
||||
@@ -3,6 +3,12 @@ using Bit.Core.Exceptions;
|
||||
|
||||
namespace Bit.Core.KeyManagement.Models.Data;
|
||||
|
||||
/// <summary>
|
||||
/// The data used for authentication of a master password.
|
||||
///
|
||||
/// This data model does not have any validation, consider using MasterPasswordAuthenticationDataRequestModel
|
||||
/// if validation is required.
|
||||
/// </summary>
|
||||
public class MasterPasswordAuthenticationData
|
||||
{
|
||||
public required KdfSettings Kdf { get; init; }
|
||||
@@ -16,17 +22,4 @@ public class MasterPasswordAuthenticationData
|
||||
throw new BadRequestException("Invalid master password salt.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowIfExistsAndHashIsNotEqual(
|
||||
MasterPasswordAuthenticationData? authenticationData,
|
||||
string? hash)
|
||||
{
|
||||
if (authenticationData != null && hash != null)
|
||||
{
|
||||
if (authenticationData.MasterPasswordAuthenticationHash != hash)
|
||||
{
|
||||
throw new Exception("Master password hash and hash are not equal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,31 +16,4 @@ public class MasterPasswordUnlockData
|
||||
throw new BadRequestException("Invalid master password salt.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowIfExistsAndNotMatchingAuthenticationData(
|
||||
MasterPasswordAuthenticationData? authenticationData,
|
||||
MasterPasswordUnlockData? unlockData)
|
||||
{
|
||||
if (unlockData != null && authenticationData != null)
|
||||
{
|
||||
var matches = MatchesAuthenticationData(
|
||||
unlockData,
|
||||
authenticationData);
|
||||
|
||||
if (!matches)
|
||||
{
|
||||
throw new Exception("KDF settings and salt must match between authentication and unlock data.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool MatchesAuthenticationData(
|
||||
MasterPasswordUnlockData unlockData,
|
||||
MasterPasswordAuthenticationData authenticationData)
|
||||
{
|
||||
var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf);
|
||||
var saltMatches = unlockData.Salt == authenticationData.Salt;
|
||||
|
||||
return kdfMatches && saltMatches;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Models.Api.Request;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
@@ -603,31 +604,28 @@ public class AccountsControllerTests : IDisposable
|
||||
string encryptedPrivateKey)
|
||||
{
|
||||
// Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData)
|
||||
|
||||
var kdfData = new KdfRequestModel
|
||||
{
|
||||
KdfType = KdfType.Argon2id,
|
||||
Iterations = AuthConstants.ARGON2_ITERATIONS.Default,
|
||||
Memory = AuthConstants.ARGON2_MEMORY.Default,
|
||||
Parallelism = AuthConstants.ARGON2_PARALLELISM.Default
|
||||
};
|
||||
|
||||
var newModel = new RegisterFinishRequestModel
|
||||
{
|
||||
Email = email,
|
||||
EmailVerificationToken = emailVerificationToken,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
Kdf = new KdfSettings
|
||||
{
|
||||
KdfType = KdfType.Argon2id,
|
||||
Iterations = AuthConstants.ARGON2_ITERATIONS.Default,
|
||||
Memory = AuthConstants.ARGON2_MEMORY.Default,
|
||||
Parallelism = AuthConstants.ARGON2_PARALLELISM.Default
|
||||
},
|
||||
Kdf = kdfData,
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email // salt choice is not validated here during registration
|
||||
},
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockData
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
Kdf = new KdfSettings
|
||||
{
|
||||
KdfType = KdfType.Argon2id,
|
||||
Iterations = AuthConstants.ARGON2_ITERATIONS.Default,
|
||||
Memory = AuthConstants.ARGON2_MEMORY.Default,
|
||||
Parallelism = AuthConstants.ARGON2_PARALLELISM.Default
|
||||
},
|
||||
Kdf = kdfData,
|
||||
MasterKeyWrappedUserKey = masterKeyWrappedUserKey,
|
||||
Salt = email
|
||||
},
|
||||
@@ -719,29 +717,29 @@ public class AccountsControllerTests : IDisposable
|
||||
string publicKey,
|
||||
string encryptedPrivateKey)
|
||||
{
|
||||
var kdfData = new KdfRequestModel
|
||||
{
|
||||
KdfType = KdfType.Argon2id,
|
||||
Iterations = AuthConstants.ARGON2_ITERATIONS.Default,
|
||||
Memory = AuthConstants.ARGON2_MEMORY.Default,
|
||||
Parallelism = AuthConstants.ARGON2_PARALLELISM.Default
|
||||
};
|
||||
|
||||
// Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData)
|
||||
var newModel = new RegisterFinishRequestModel
|
||||
{
|
||||
Email = email,
|
||||
OrgInviteToken = orgInviteToken,
|
||||
OrganizationUserId = organizationUserId,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
Kdf = new KdfSettings
|
||||
{
|
||||
KdfType = KdfType.PBKDF2_SHA256,
|
||||
Iterations = AuthConstants.PBKDF2_ITERATIONS.Default
|
||||
},
|
||||
Kdf = kdfData,
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email
|
||||
},
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockData
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
Kdf = new KdfSettings
|
||||
{
|
||||
KdfType = KdfType.PBKDF2_SHA256,
|
||||
Iterations = AuthConstants.PBKDF2_ITERATIONS.Default
|
||||
},
|
||||
Kdf = kdfData,
|
||||
MasterKeyWrappedUserKey = masterKeyWrappedUserKey,
|
||||
Salt = email
|
||||
},
|
||||
@@ -759,8 +757,10 @@ public class AccountsControllerTests : IDisposable
|
||||
OrgInviteToken = orgInviteToken,
|
||||
OrganizationUserId = organizationUserId,
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
Kdf = kdfData.KdfType,
|
||||
KdfIterations = kdfData.Iterations,
|
||||
KdfMemory = kdfData.Memory,
|
||||
KdfParallelism = kdfData.Parallelism,
|
||||
UserSymmetricKey = masterKeyWrappedUserKey,
|
||||
UserAsymmetricKeys = new KeysRequestModel
|
||||
{
|
||||
@@ -832,7 +832,7 @@ public class AccountsControllerTests : IDisposable
|
||||
string encryptedPrivateKey)
|
||||
{
|
||||
// Arrange: Provide only unlock-data KDF + key; leave root KDF fields null
|
||||
var unlockKdf = new KdfSettings
|
||||
var unlockKdf = new KdfRequestModel
|
||||
{
|
||||
KdfType = KdfType.PBKDF2_SHA256,
|
||||
Iterations = iterations
|
||||
@@ -842,14 +842,14 @@ public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
Email = email,
|
||||
EmailVerificationToken = emailVerificationToken,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
// present but not used by ToUser for KDF/Key
|
||||
Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations },
|
||||
Kdf = unlockKdf,
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email
|
||||
},
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockData
|
||||
MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
Kdf = unlockKdf,
|
||||
MasterKeyWrappedUserKey = masterKeyWrappedUserKey,
|
||||
@@ -942,10 +942,10 @@ public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
Email = email,
|
||||
EmailVerificationToken = emailVerificationToken,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
// present but ToUser does not source KDF from here
|
||||
Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations },
|
||||
Kdf = new KdfRequestModel { KdfType = KdfType.Argon2id, Iterations = iterations },
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email
|
||||
},
|
||||
@@ -980,10 +980,10 @@ public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
Email = email,
|
||||
EmailVerificationToken = emailVerificationToken,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
// present but ToUser does not source iterations from here
|
||||
Kdf = new KdfSettings { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default },
|
||||
Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default },
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email
|
||||
},
|
||||
@@ -1018,9 +1018,9 @@ public class AccountsControllerTests : IDisposable
|
||||
{
|
||||
Email = email,
|
||||
EmailVerificationToken = emailVerificationToken,
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
Kdf = new KdfSettings { KdfType = kdfType, Iterations = iterations },
|
||||
Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = iterations },
|
||||
MasterPasswordAuthenticationHash = masterPasswordHash,
|
||||
Salt = email
|
||||
},
|
||||
|
||||
@@ -8,6 +8,7 @@ using Bit.Core;
|
||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Api.Request;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Identity;
|
||||
@@ -226,7 +227,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
effectiveParallelism = AuthConstants.ARGON2_PARALLELISM.Default;
|
||||
}
|
||||
|
||||
var alignedKdf = new KdfSettings
|
||||
var alignedKdf = new KdfRequestModel
|
||||
{
|
||||
KdfType = effectiveKdfType,
|
||||
Iterations = effectiveIterations,
|
||||
@@ -241,7 +242,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
var masterKeyWrappedUserKey = !string.IsNullOrWhiteSpace(unlock.MasterKeyWrappedUserKey)
|
||||
? unlock.MasterKeyWrappedUserKey
|
||||
: (string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey) ? "user_symmetric_key" : requestModel.UserSymmetricKey);
|
||||
requestModel.MasterPasswordUnlock = new MasterPasswordUnlockData
|
||||
requestModel.MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel
|
||||
{
|
||||
Kdf = alignedKdf,
|
||||
MasterKeyWrappedUserKey = masterKeyWrappedUserKey,
|
||||
@@ -254,7 +255,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase<Startup>
|
||||
// Ensure registration uses the same hash the tests will provide at login.
|
||||
// PM-28143 - When MasterPasswordAuthenticationData is the only source of the auth hash,
|
||||
// stop overriding it from MasterPasswordHash and delete this whole reassignment block.
|
||||
requestModel.MasterPasswordAuthentication = new MasterPasswordAuthenticationData
|
||||
requestModel.MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel
|
||||
{
|
||||
Kdf = alignedKdf,
|
||||
MasterPasswordAuthenticationHash = requestModel.MasterPasswordHash,
|
||||
|
||||
Reference in New Issue
Block a user