1
0
mirror of https://github.com/bitwarden/server synced 2026-01-30 00:03:48 +00:00

Use Scene result for SingleUserScene (#6909)

* Scenes should return resulting data in the result object

The result is for data that cannot be known by the client requesting the scene and the mangle map used for mangling input values to enable parallelizing tests

* Fix filenames

* SingleUserScene now has a return value of various created User data

* 1/100 too frequent for false test failures
This commit is contained in:
Matt Gibson
2026-01-27 12:55:04 -08:00
committed by GitHub
parent f578dab94f
commit edf694b8d4
6 changed files with 30 additions and 46 deletions

View File

@@ -98,7 +98,7 @@ public class EnumerationProtectionHelpersTests
var hmacKey = RandomNumberGenerator.GetBytes(32);
var salt1 = "user1@example.com";
var salt2 = "user2@example.com";
var range = 100;
var range = 10_000;
// Act
var result1 = EnumerationProtectionHelpers.GetIndexForInputHash(hmacKey, salt1, range);
@@ -117,7 +117,7 @@ public class EnumerationProtectionHelpersTests
var hmacKey1 = RandomNumberGenerator.GetBytes(32);
var hmacKey2 = RandomNumberGenerator.GetBytes(32);
var salt = "test@example.com";
var range = 100;
var range = 10_000;
// Act
var result1 = EnumerationProtectionHelpers.GetIndexForInputHash(hmacKey1, salt, range);

View File

@@ -45,7 +45,7 @@ public class SeedControllerTests : IClassFixture<SeederApiApplicationFactory>, I
Assert.NotNull(result);
Assert.NotNull(result.MangleMap);
Assert.Null(result.Result);
Assert.NotNull(result.Result);
}
[Fact]

View File

@@ -1,5 +1,4 @@
using System.Globalization;
using Bit.Core.Entities;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Bit.RustSDK;
@@ -10,13 +9,6 @@ namespace Bit.Seeder.Factories;
public struct UserData
{
public string Email;
public Guid Id;
public string? Key;
public string? PublicKey;
public string? PrivateKey;
public string? ApiKey;
public KdfType Kdf;
public int KdfIterations;
}
public class UserSeeder(RustSdkService sdkService, IPasswordHasher<Bit.Core.Entities.User> passwordHasher, MangleId mangleId)
@@ -75,30 +67,8 @@ public class UserSeeder(RustSdkService sdkService, IPasswordHasher<Bit.Core.Enti
{
var mangleMap = new Dictionary<string, string?>
{
{ expectedUserData.Email, MangleEmail(expectedUserData.Email) },
{ expectedUserData.Id.ToString(), user.Id.ToString() },
{ expectedUserData.Kdf.ToString(), user.Kdf.ToString() },
{ expectedUserData.KdfIterations.ToString(CultureInfo.InvariantCulture), user.KdfIterations.ToString(CultureInfo.InvariantCulture) }
{ expectedUserData.Email, user.Email },
};
if (expectedUserData.Key != null)
{
mangleMap[expectedUserData.Key] = user.Key;
}
if (expectedUserData.PublicKey != null)
{
mangleMap[expectedUserData.PublicKey] = user.PublicKey;
}
if (expectedUserData.PrivateKey != null)
{
mangleMap[expectedUserData.PrivateKey] = user.PrivateKey;
}
if (expectedUserData.ApiKey != null)
{
mangleMap[expectedUserData.ApiKey] = user.ApiKey;
}
return mangleMap;
}

View File

@@ -72,7 +72,7 @@ public interface IScene<TRequest> : IScene where TRequest : class
/// and entity tracking information. The explicit interface implementations allow dynamic invocation
/// while preserving type safety in the implementation.
/// </remarks>
public interface IScene<TRequest, TResult> : IScene where TRequest : class where TResult : class
public interface IScene<TRequest, TResult> : IScene where TRequest : class
{
/// <summary>
/// Seeds data based on the provided strongly-typed request and returns typed result data.

View File

@@ -4,10 +4,22 @@ using Bit.Seeder.Factories;
namespace Bit.Seeder.Scenes;
public struct SingleUserSceneResult
{
public Guid UserId { get; init; }
public string Kdf { get; init; }
public int KdfIterations { get; init; }
public string Key { get; init; }
public string PublicKey { get; init; }
public string PrivateKey { get; init; }
public string ApiKey { get; init; }
}
/// <summary>
/// Creates a single user using the provided account details.
/// </summary>
public class SingleUserScene(UserSeeder userSeeder, IUserRepository userRepository) : IScene<SingleUserScene.Request>
public class SingleUserScene(UserSeeder userSeeder, IUserRepository userRepository) : IScene<SingleUserScene.Request, SingleUserSceneResult>
{
public class Request
{
@@ -17,22 +29,24 @@ public class SingleUserScene(UserSeeder userSeeder, IUserRepository userReposito
public bool Premium { get; set; } = false;
}
public async Task<SceneResult> SeedAsync(Request request)
public async Task<SceneResult<SingleUserSceneResult>> SeedAsync(Request request)
{
var user = userSeeder.CreateUser(request.Email, request.EmailVerified, request.Premium);
await userRepository.CreateAsync(user);
return new SceneResult(mangleMap: userSeeder.GetMangleMap(user, new UserData
return new SceneResult<SingleUserSceneResult>(result: new SingleUserSceneResult
{
UserId = user.Id,
Kdf = user.Kdf.ToString(),
KdfIterations = user.KdfIterations,
Key = user.Key!,
PublicKey = user.PublicKey!,
PrivateKey = user.PrivateKey!,
ApiKey = user.ApiKey!,
}, mangleMap: userSeeder.GetMangleMap(user, new UserData
{
Email = request.Email,
Id = user.Id,
Key = user.Key,
PublicKey = user.PublicKey,
PrivateKey = user.PrivateKey,
ApiKey = user.ApiKey,
Kdf = user.Kdf,
KdfIterations = user.KdfIterations,
}));
}
}