diff --git a/util/Seeder/Factories/LoginCipherSeeder.cs b/util/Seeder/Factories/LoginCipherSeeder.cs index 4683a2c7b6b4..6e557476fb76 100644 --- a/util/Seeder/Factories/LoginCipherSeeder.cs +++ b/util/Seeder/Factories/LoginCipherSeeder.cs @@ -15,6 +15,8 @@ internal static Cipher Create( string? password = null, string? uri = null, string? notes = null, + bool reprompt = false, + bool deleted = false, IEnumerable<(string name, string value, int type)>? fields = null) { var cipherView = new CipherViewDto @@ -29,6 +31,8 @@ internal static Cipher Create( Password = password, Uris = string.IsNullOrEmpty(uri) ? null : [new LoginUriViewDto { Uri = uri }] }, + Reprompt = reprompt ? RepromptTypes.Password : RepromptTypes.None, + DeletedDate = deleted ? DateTime.UtcNow.AddDays(-1) : null, Fields = fields?.Select(f => new FieldViewDto { Name = f.name, diff --git a/util/Seeder/Factories/SshKeyCipherSeeder.cs b/util/Seeder/Factories/SshKeyCipherSeeder.cs index fa70a8bcbc2f..10a6a1bba254 100644 --- a/util/Seeder/Factories/SshKeyCipherSeeder.cs +++ b/util/Seeder/Factories/SshKeyCipherSeeder.cs @@ -12,7 +12,8 @@ internal static Cipher Create( SshKeyViewDto sshKey, Guid? organizationId = null, Guid? userId = null, - string? notes = null) + string? notes = null, + bool reprompt = false) { var cipherView = new CipherViewDto { @@ -20,7 +21,8 @@ internal static Cipher Create( Name = name, Notes = notes, Type = CipherTypes.SshKey, - SshKey = sshKey + SshKey = sshKey, + Reprompt = reprompt ? RepromptTypes.Password : RepromptTypes.None, }; var encrypted = CipherEncryption.Encrypt(cipherView, encryptionKey); diff --git a/util/Seeder/Scenes/UserCardCipherScene.cs b/util/Seeder/Scenes/UserCardCipherScene.cs new file mode 100644 index 000000000000..93a0e0e26b10 --- /dev/null +++ b/util/Seeder/Scenes/UserCardCipherScene.cs @@ -0,0 +1,60 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Repositories; +using Bit.Core.Vault.Repositories; +using Bit.Seeder.Factories; +using Bit.Seeder.Models; +using Bit.Seeder.Services; + +namespace Bit.Seeder.Scenes; + +public class UserCardCipherScene(IUserRepository userRepository, ICipherRepository cipherRepository, IManglerService manglerService) : IScene +{ + public class Request + { + [Required] + public required Guid UserId { get; set; } + [Required] + public required string UserKeyB64 { get; set; } + [Required] + public required string Name { get; set; } + public required string CardholderName { get; set; } + public required string Number { get; set; } + public required string ExpMonth { get; set; } + public required string ExpYear { get; set; } + public required string Code { get; set; } + public string? Notes { get; set; } + } + + public class Result + { + public required Guid CipherId { get; set; } + } + + public async Task> SeedAsync(Request request) + { + var user = await userRepository.GetByIdAsync(request.UserId); + if (user == null) + { + throw new Exception($"User with ID {request.UserId} not found."); + } + + var card = new CardViewDto + { + CardholderName = request.CardholderName, + Number = request.Number, + ExpMonth = request.ExpMonth, + ExpYear = request.ExpYear, + Code = request.Code + }; + var cipher = CardCipherSeeder.Create(request.UserKeyB64, request.Name, card: card, userId: request.UserId, notes: request.Notes); + + await cipherRepository.CreateAsync(cipher); + + return new SceneResult( + result: new Result + { + CipherId = cipher.Id + }, + mangleMap: manglerService.GetMangleMap()); + } +} diff --git a/util/Seeder/Scenes/UserIdentityCipherScene.cs b/util/Seeder/Scenes/UserIdentityCipherScene.cs new file mode 100644 index 000000000000..7adf9662d28a --- /dev/null +++ b/util/Seeder/Scenes/UserIdentityCipherScene.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Repositories; +using Bit.Core.Vault.Repositories; +using Bit.Seeder.Factories; +using Bit.Seeder.Models; +using Bit.Seeder.Services; + +namespace Bit.Seeder.Scenes; + +public class UserIdentityCipherScene(IUserRepository userRepository, ICipherRepository cipherRepository, IManglerService manglerService) : IScene +{ + public class Request + { + [Required] + public required Guid UserId { get; set; } + [Required] + public required string UserKeyB64 { get; set; } + [Required] + public required string Name { get; set; } + public string? Title { get; set; } + public string? FirstName { get; set; } + public string? MiddleName { get; set; } + public string? LastName { get; set; } + public string? Notes { get; set; } + } + + public class Result + { + public required Guid CipherId { get; set; } + } + + public async Task> SeedAsync(Request request) + { + var user = await userRepository.GetByIdAsync(request.UserId); + if (user == null) + { + throw new Exception($"User with ID {request.UserId} not found."); + } + + var identity = new IdentityViewDto + { + Title = request.Title, + FirstName = request.FirstName, + MiddleName = request.MiddleName, + LastName = request.LastName + }; + var cipher = IdentityCipherSeeder.Create(request.UserKeyB64, request.Name, userId: request.UserId, identity: identity, notes: request.Notes); + + await cipherRepository.CreateAsync(cipher); + + return new SceneResult( + result: new Result + { + CipherId = cipher.Id + }, + mangleMap: manglerService.GetMangleMap()); + } +} diff --git a/util/Seeder/Scenes/UserLoginCipherScene.cs b/util/Seeder/Scenes/UserLoginCipherScene.cs new file mode 100644 index 000000000000..2cbbd24bd223 --- /dev/null +++ b/util/Seeder/Scenes/UserLoginCipherScene.cs @@ -0,0 +1,52 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Repositories; +using Bit.Core.Vault.Repositories; +using Bit.Seeder.Factories; +using Bit.Seeder.Services; + +namespace Bit.Seeder.Scenes; + +public class UserLoginCipherScene(IUserRepository userRepository, ICipherRepository cipherRepository, IManglerService manglerService) : IScene +{ + public class Request + { + [Required] + public required Guid UserId { get; set; } + [Required] + public required string UserKeyB64 { get; set; } + [Required] + public required string Name { get; set; } + public string? Username { get; set; } + public string? Password { get; set; } + public string? Uri { get; set; } + public string? Notes { get; set; } + public bool Reprompt { get; set; } + public bool Deleted { get; set; } + public IEnumerable<(string name, string value, int type)>? Fields { get; set; } + } + + public class Result + { + public required Guid CipherId { get; set; } + } + + public async Task> SeedAsync(Request request) + { + var user = await userRepository.GetByIdAsync(request.UserId); + if (user == null) + { + throw new Exception($"User with ID {request.UserId} not found."); + } + + var cipher = LoginCipherSeeder.Create(request.UserKeyB64, request.Name, userId: request.UserId, username: request.Username, password: request.Password, uri: request.Uri, notes: request.Notes, fields: request.Fields, reprompt: request.Reprompt, deleted: request.Deleted); + + await cipherRepository.CreateAsync(cipher); + + return new SceneResult( + result: new Result + { + CipherId = cipher.Id + }, + mangleMap: manglerService.GetMangleMap()); + } +} diff --git a/util/Seeder/Scenes/UserSecureNoteCipherScene.cs b/util/Seeder/Scenes/UserSecureNoteCipherScene.cs new file mode 100644 index 000000000000..ec99ab9d680c --- /dev/null +++ b/util/Seeder/Scenes/UserSecureNoteCipherScene.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Repositories; +using Bit.Core.Vault.Repositories; +using Bit.Seeder.Factories; +using Bit.Seeder.Services; + +namespace Bit.Seeder.Scenes; + +public class UserSecureNoteCipherScene(IUserRepository userRepository, ICipherRepository cipherRepository, IManglerService manglerService) : IScene +{ + public class Request + { + [Required] + public required Guid UserId { get; set; } + [Required] + public required string UserKeyB64 { get; set; } + [Required] + public required string Name { get; set; } + public string? Notes { get; set; } + } + + public class Result + { + public required Guid CipherId { get; set; } + } + + public async Task> SeedAsync(Request request) + { + var user = await userRepository.GetByIdAsync(request.UserId); + if (user == null) + { + throw new Exception($"User with ID {request.UserId} not found."); + } + + var cipher = SecureNoteCipherSeeder.Create(request.UserKeyB64, request.Name, userId: request.UserId, notes: request.Notes); + + await cipherRepository.CreateAsync(cipher); + + return new SceneResult( + result: new Result + { + CipherId = cipher.Id + }, + mangleMap: manglerService.GetMangleMap()); + } +} diff --git a/util/Seeder/Scenes/UserSshKeyCipherScene.cs b/util/Seeder/Scenes/UserSshKeyCipherScene.cs new file mode 100644 index 000000000000..726424df8675 --- /dev/null +++ b/util/Seeder/Scenes/UserSshKeyCipherScene.cs @@ -0,0 +1,57 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.Repositories; +using Bit.Core.Vault.Repositories; +using Bit.Seeder.Factories; +using Bit.Seeder.Models; +using Bit.Seeder.Services; + +namespace Bit.Seeder.Scenes; + +public class UserSshKeyCipherScene(IUserRepository userRepository, ICipherRepository cipherRepository, IManglerService manglerService) : IScene +{ + public class Request + { + [Required] + public required Guid UserId { get; set; } + [Required] + public required string UserKeyB64 { get; set; } + [Required] + public required string Name { get; set; } + public string? PrivateKey { get; set; } + public string? PublicKey { get; set; } + public string? Fingerprint { get; set; } + public bool Reprompt { get; set; } + public string? Notes { get; set; } + } + + public class Result + { + public required Guid CipherId { get; set; } + } + + public async Task> SeedAsync(Request request) + { + var user = await userRepository.GetByIdAsync(request.UserId); + if (user == null) + { + throw new Exception($"User with ID {request.UserId} not found."); + } + + var sshKey = new SshKeyViewDto + { + PrivateKey = request.PrivateKey, + PublicKey = request.PublicKey, + Fingerprint = request.Fingerprint + }; + var cipher = SshKeyCipherSeeder.Create(request.UserKeyB64, request.Name, userId: request.UserId, sshKey: sshKey, notes: request.Notes, reprompt: request.Reprompt); + + await cipherRepository.CreateAsync(cipher); + + return new SceneResult( + result: new Result + { + CipherId = cipher.Id + }, + mangleMap: manglerService.GetMangleMap()); + } +} diff --git a/util/SeederApi/Commands/DestroyBatchScenesCommand.cs b/util/SeederApi/Commands/DestroyBatchScenesCommand.cs index 50f6142a988d..aeb7e05aca16 100644 --- a/util/SeederApi/Commands/DestroyBatchScenesCommand.cs +++ b/util/SeederApi/Commands/DestroyBatchScenesCommand.cs @@ -10,7 +10,7 @@ public async Task DestroyAsync(IEnumerable playIds) { var exceptions = new List(); - var deleteTasks = playIds.Select(async playId => + foreach (var playId in playIds) { try { @@ -18,15 +18,10 @@ public async Task DestroyAsync(IEnumerable playIds) } catch (Exception ex) { - lock (exceptions) - { - exceptions.Add(ex); - } + exceptions.Add(ex); logger.LogError(ex, "Error deleting seeded data: {PlayId}", playId); } - }); - - await Task.WhenAll(deleteTasks); + } if (exceptions.Count > 0) {