using Bit.Core.Entities; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Enums; using Bit.Seeder.Data; using Bit.Seeder.Data.Distributions; using Bit.Seeder.Data.Enums; using Bit.Seeder.Data.Generators; using Bit.Seeder.Data.Static; using Bit.Seeder.Factories; using Bit.Seeder.Pipeline; namespace Bit.Seeder.Steps; /// /// Creates N random cipher entities using the deterministic . /// /// /// Requires to have run first. Picks cipher types (login, card, /// identity, secureNote, sshKey) from a configurable distribution, delegates to the existing /// cipher factories, and assigns each cipher to collections round-robin. Designed for load /// testing scenarios where you need thousands of realistic vault items. /// /// /// internal sealed class GenerateCiphersStep( int count, Distribution? typeDist = null, Distribution? pwDist = null) : IStep { public void Execute(SeederContext context) { if (count == 0) { return; } var generator = context.RequireGenerator(); var orgId = context.RequireOrgId(); var orgKey = context.RequireOrgKey(); var collectionIds = context.Registry.CollectionIds; var typeDistribution = typeDist ?? CipherTypeDistributions.Realistic; var passwordDistribution = pwDist ?? PasswordDistributions.Realistic; var companies = Companies.All; var ciphers = new List(count); var cipherIds = new List(count); var collectionCiphers = new List(); for (var i = 0; i < count; i++) { var cipherType = typeDistribution.Select(i, count); var cipher = cipherType switch { CipherType.Login => CreateLoginCipher(i, orgId, orgKey, companies, generator, passwordDistribution), CipherType.Card => CreateCardCipher(i, orgId, orgKey, generator), CipherType.Identity => CreateIdentityCipher(i, orgId, orgKey, generator), CipherType.SecureNote => CreateSecureNoteCipher(i, orgId, orgKey, generator), CipherType.SSHKey => CreateSshKeyCipher(i, orgId, orgKey), _ => throw new ArgumentException($"Unsupported cipher type: {cipherType}") }; ciphers.Add(cipher); cipherIds.Add(cipher.Id); // Collection assignment if (collectionIds.Count <= 0) { continue; } collectionCiphers.Add(new CollectionCipher { CipherId = cipher.Id, CollectionId = collectionIds[i % collectionIds.Count] }); // Every 3rd cipher gets assigned to an additional collection if (i % 3 == 0 && collectionIds.Count > 1) { collectionCiphers.Add(new CollectionCipher { CipherId = cipher.Id, CollectionId = collectionIds[(i + 1) % collectionIds.Count] }); } } context.Ciphers.AddRange(ciphers); context.Registry.CipherIds.AddRange(cipherIds); context.CollectionCiphers.AddRange(collectionCiphers); } private static Cipher CreateLoginCipher( int index, Guid organizationId, string orgKey, Company[] companies, GeneratorContext generator, Distribution passwordDistribution) { var company = companies[index % companies.Length]; return LoginCipherSeeder.Create( orgKey, name: $"{company.Name} ({company.Category})", organizationId: organizationId, username: generator.Username.GenerateByIndex(index, totalHint: generator.CipherCount, domain: company.Domain), password: Passwords.GetPassword(index, generator.CipherCount, passwordDistribution), uri: $"https://{company.Domain}"); } private static Cipher CreateCardCipher(int index, Guid organizationId, string orgKey, GeneratorContext generator) { var card = generator.Card.GenerateByIndex(index); return CardCipherSeeder.Create( orgKey, name: $"{card.CardholderName}'s {card.Brand}", card: card, organizationId: organizationId); } private static Cipher CreateIdentityCipher(int index, Guid organizationId, string orgKey, GeneratorContext generator) { var identity = generator.Identity.GenerateByIndex(index); var name = $"{identity.FirstName} {identity.LastName}"; if (!string.IsNullOrEmpty(identity.Company)) { name += $" ({identity.Company})"; } return IdentityCipherSeeder.Create( orgKey, name: name, identity: identity, organizationId: organizationId); } private static Cipher CreateSecureNoteCipher(int index, Guid organizationId, string orgKey, GeneratorContext generator) { var (name, notes) = generator.SecureNote.GenerateByIndex(index); return SecureNoteCipherSeeder.Create( orgKey, name: name, organizationId: organizationId, notes: notes); } private static Cipher CreateSshKeyCipher(int index, Guid organizationId, string orgKey) { var sshKey = SshKeyDataGenerator.GenerateByIndex(index); return SshKeyCipherSeeder.Create( orgKey, name: $"SSH Key {index + 1}", sshKey: sshKey, organizationId: organizationId); } }