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;
///
/// Creates member users and links them to the current organization.
/// When realisticStatusMix is enabled (and count >= 10), users receive a
/// realistic distribution of Confirmed/Invited/Accepted/Revoked statuses.
///
internal sealed class CreateUsersStep(int count, bool realisticStatusMix = false) : IStep
{
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 users = new List(count);
var organizationUsers = new List(count);
var hardenedOrgUserIds = new List();
var userDigests = new List();
for (var i = 0; i < count; i++)
{
var email = $"user{i}@{domain}";
var mangledEmail = context.GetMangler().Mangle(email);
var userKeys = RustSdkService.GenerateUserKeys(mangledEmail, UserSeeder.DefaultPassword);
var user = UserSeeder.Create(mangledEmail, context.GetPasswordHasher(), context.GetMangler(), keys: userKeys);
var status = statusDistribution.Select(i, count);
var memberOrgKey = StatusRequiresOrgKey(status)
? RustSdkService.GenerateUserOrganizationKey(user.PublicKey!, orgKey)
: null;
var orgUser = org.CreateOrganizationUserWithKey(
user, OrganizationUserType.User, status, memberOrgKey);
users.Add(user);
organizationUsers.Add(orgUser);
if (status == OrganizationUserStatusType.Confirmed)
{
hardenedOrgUserIds.Add(orgUser.Id);
userDigests.Add(new EntityRegistry.UserDigest(user.Id, orgUser.Id, userKeys.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;
}