The core infrastructure backend (API, database, Docker, etc).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

85 lines
3.4 KiB

using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.RustSDK;
using Bit.Seeder.Data.Distributions;
using Bit.Seeder.Factories;
using Bit.Seeder.Pipeline;
namespace Bit.Seeder.Steps;
/// <summary>
/// Creates member users and links them to the current organization.
/// When <c>realisticStatusMix</c> is enabled (and count >= 10), users receive a
/// realistic distribution of Confirmed/Invited/Accepted/Revoked statuses.
/// </summary>
internal sealed class CreateUsersStep(int count, bool realisticStatusMix = false) : IStep
{
private const int RsaPoolSize = 100;
public void Execute(SeederContext context)
{
var org = context.RequireOrganization();
var orgKey = context.RequireOrgKey();
var domain = context.RequireDomain();
var statusDistribution = realisticStatusMix && count >= 10
? UserStatusDistributions.Realistic
: UserStatusDistributions.AllConfirmed;
var password = context.GetPassword();
var kdfIterations = context.GetKdfIterations();
var mangler = context.GetMangler();
var passwordHasher = context.GetPasswordHasher();
// Pre-compute mangled emails and statuses (ManglerService is not thread-safe)
var mangledEmails = new string[count];
var statuses = new OrganizationUserStatusType[count];
for (var i = 0; i < count; i++)
{
mangledEmails[i] = mangler.Mangle($"user{i}@{domain}");
statuses[i] = statusDistribution.Select(i, count);
}
var results = new (User User, OrganizationUser OrgUser, UserKeys Keys, bool IsConfirmed)[count];
Parallel.For(0, count, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, i =>
{
var userKeys = RustSdkService.GenerateUserKeys(mangledEmails[i], password, kdfIterations, (uint)(i % RsaPoolSize));
var (user, _) = UserSeeder.Create(mangledEmails[i], passwordHasher, mangler, keys: userKeys, password: password, kdfIterations: kdfIterations);
var memberOrgKey = StatusRequiresOrgKey(statuses[i])
? RustSdkService.GenerateUserOrganizationKey(user.PublicKey!, orgKey)
: null;
var orgUser = org.CreateOrganizationUserWithKey(
user, OrganizationUserType.User, statuses[i], memberOrgKey);
results[i] = (user, orgUser, userKeys, statuses[i] == OrganizationUserStatusType.Confirmed);
});
var users = new List<User>(count);
var organizationUsers = new List<OrganizationUser>(count);
var hardenedOrgUserIds = new List<Guid>(count);
var userDigests = new List<EntityRegistry.UserDigest>(count);
for (var i = 0; i < count; i++)
{
var r = results[i];
users.Add(r.User);
organizationUsers.Add(r.OrgUser);
if (r.IsConfirmed)
{
hardenedOrgUserIds.Add(r.OrgUser.Id);
userDigests.Add(new EntityRegistry.UserDigest(r.User.Id, r.OrgUser.Id, r.Keys.Key));
}
}
context.Users.AddRange(users);
context.OrganizationUsers.AddRange(organizationUsers);
context.Registry.HardenedOrgUserIds.AddRange(hardenedOrgUserIds);
context.Registry.UserDigests.AddRange(userDigests);
}
private static bool StatusRequiresOrgKey(OrganizationUserStatusType status) =>
status is OrganizationUserStatusType.Confirmed or OrganizationUserStatusType.Revoked;
}