From a4aa40c983bba29eeaf08dc483774cfb41b49449 Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 06:28:39 -0600 Subject: [PATCH 1/6] Initial commit --- util/DBSeeder/DBSeeder.csproj | 14 +++ util/DBSeeder/EFDBSeeder.cs | 104 ++++++++++++++++++ util/DBSeeder/Factories.cs | 30 +++++ .../EFDBSeederUtility.csproj | 18 +++ util/EFDBSeederUtility/Program.cs | 39 +++++++ 5 files changed, 205 insertions(+) create mode 100644 util/DBSeeder/DBSeeder.csproj create mode 100644 util/DBSeeder/EFDBSeeder.cs create mode 100644 util/DBSeeder/Factories.cs create mode 100644 util/EFDBSeederUtility/EFDBSeederUtility.csproj create mode 100644 util/EFDBSeederUtility/Program.cs diff --git a/util/DBSeeder/DBSeeder.csproj b/util/DBSeeder/DBSeeder.csproj new file mode 100644 index 000000000000..0b14f1b3943b --- /dev/null +++ b/util/DBSeeder/DBSeeder.csproj @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/util/DBSeeder/EFDBSeeder.cs b/util/DBSeeder/EFDBSeeder.cs new file mode 100644 index 000000000000..1d165f3c193b --- /dev/null +++ b/util/DBSeeder/EFDBSeeder.cs @@ -0,0 +1,104 @@ +using System; +using System.Linq; +using System.Data; +using System.Reflection; +using System.Text; +using Bit.Core; +using Bit.Infrastructure.EntityFramework.Models; // Change this line +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace Bit.DBSeeder; + +public class EFDBSeeder +{ + private readonly string _connectionString; + private readonly string _databaseProvider; + private readonly ILogger _logger; + + public EFDBSeeder(string connectionString, string databaseProvider) + { + _connectionString = connectionString; + _databaseProvider = databaseProvider; + + } + + public bool SeedDatabase() + { + //print connectionstring to console + Console.WriteLine(_connectionString); + Console.WriteLine(_databaseProvider); + + try + { + var factory = new DatabaseContextFactory(); + using (var context = factory.CreateDbContext(new[] { _connectionString })) + { + if (context.Database.CanConnect()) + { + Console.WriteLine("Successfully connected to the database!"); + + // Seed the database + SeedUsers(context); + + Console.WriteLine("Database seeded successfully!"); + } + else + { + Console.WriteLine("Failed to connect to the database."); + return false; + } + } + } + catch (Exception ex) + { + Console.WriteLine($"An error occurred: {ex.Message}"); + return false; + } + + + + return true; + } + + private void SeedUsers(DatabaseContext context) + { + if (!context.Users.Any()) + { + context.Users.AddRange( + new Bit.Infrastructure.EntityFramework.Models.User // Specify the full namespace + { + Id = Guid.NewGuid(), + Name = "Test User", + Email = "testuser@example.com", + EmailVerified = true, + SecurityStamp = Guid.NewGuid().ToString(), + ApiKey = "TestApiKey" + } + ); + context.SaveChanges(); + Console.WriteLine("Test user added to the database."); + } + else + { + Console.WriteLine("Users table is not empty. Skipping user seeding."); + } + } + + /* private ILogger CreateLogger() + { + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddConsole(); + + builder.AddFilter("EFDBSeeder.EFDBSeeder", LogLevel.Information); + }); + + return loggerFactory.CreateLogger(); + } + */ +} diff --git a/util/DBSeeder/Factories.cs b/util/DBSeeder/Factories.cs new file mode 100644 index 000000000000..2e723ab40706 --- /dev/null +++ b/util/DBSeeder/Factories.cs @@ -0,0 +1,30 @@ +using Bit.Core.Settings; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.DBSeeder; + + + public class DatabaseContextFactory : IDesignTimeDbContextFactory + { + public DatabaseContext CreateDbContext(string[] args) + { + if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0])) + { + throw new ArgumentException("Connection string must be provided as the first argument."); + } + + var connectionString = args[0]; + + var services = new ServiceCollection(); + services.AddDataProtection(); + services.AddDbContext(options => + options.UseSqlServer(connectionString)); + var serviceProvider = services.BuildServiceProvider(); + + return serviceProvider.GetRequiredService(); + } + } diff --git a/util/EFDBSeederUtility/EFDBSeederUtility.csproj b/util/EFDBSeederUtility/EFDBSeederUtility.csproj new file mode 100644 index 000000000000..90ad68b26711 --- /dev/null +++ b/util/EFDBSeederUtility/EFDBSeederUtility.csproj @@ -0,0 +1,18 @@ + + + + Exe + true + + + + + + + + + + + + + diff --git a/util/EFDBSeederUtility/Program.cs b/util/EFDBSeederUtility/Program.cs new file mode 100644 index 000000000000..fc50e00697ed --- /dev/null +++ b/util/EFDBSeederUtility/Program.cs @@ -0,0 +1,39 @@ +using Bit.DBSeeder; +using CommandDotNet; + +internal class Program +{ + private static int Main(string[] args) + { + return new AppRunner().Run(args); + } + + [DefaultCommand] + public void Execute( + + + + + + + [Operand(Description = "Database provider (mssql, mysql, postgres, sqlite).")] string databaseProvider, + [Operand(Description = "Database connection string.")] string ConnectionString + + + + + + ) => SeedDatabase(ConnectionString, databaseProvider); + + private static bool SeedDatabase(string databaseConnectionString, + string databaseProvider) +{ + var seeder = new EFDBSeeder(databaseConnectionString, databaseProvider); + bool success; + + success = seeder.SeedDatabase(); // Change this line + + + return success; +} +} \ No newline at end of file From d21998c471889dcd8520632556bfc0aa142737e3 Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 06:45:43 -0600 Subject: [PATCH 2/6] added projects to solution --- bitwarden-server.sln | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/bitwarden-server.sln b/bitwarden-server.sln index 154ffe36149a..b1e9b0d9be16 100644 --- a/bitwarden-server.sln +++ b/bitwarden-server.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29102.190 @@ -124,6 +124,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventsProcessor.Test", "tes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notifications.Test", "test\Notifications.Test\Notifications.Test.csproj", "{90D85D8F-5577-4570-A96E-5A2E185F0F6F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DBSeeder", "util\DBSeeder\DBSeeder.csproj", "{29318B0D-E753-4A27-BFBC-F7566FE26E2F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFDBSeederUtility", "util\EFDBSeederUtility\EFDBSeederUtility.csproj", "{47F03C4D-C178-4F9A-99B1-C7E35342D8BC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -308,6 +312,14 @@ Global {90D85D8F-5577-4570-A96E-5A2E185F0F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {90D85D8F-5577-4570-A96E-5A2E185F0F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {90D85D8F-5577-4570-A96E-5A2E185F0F6F}.Release|Any CPU.Build.0 = Release|Any CPU + {29318B0D-E753-4A27-BFBC-F7566FE26E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29318B0D-E753-4A27-BFBC-F7566FE26E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29318B0D-E753-4A27-BFBC-F7566FE26E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29318B0D-E753-4A27-BFBC-F7566FE26E2F}.Release|Any CPU.Build.0 = Release|Any CPU + {47F03C4D-C178-4F9A-99B1-C7E35342D8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47F03C4D-C178-4F9A-99B1-C7E35342D8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47F03C4D-C178-4F9A-99B1-C7E35342D8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47F03C4D-C178-4F9A-99B1-C7E35342D8BC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -357,6 +369,8 @@ Global {916AFD8C-30AF-49B6-A5C9-28CA1B5D9298} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} {81673EFB-7134-4B4B-A32F-1EA05F0EF3CE} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} {90D85D8F-5577-4570-A96E-5A2E185F0F6F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} + {29318B0D-E753-4A27-BFBC-F7566FE26E2F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E} + {47F03C4D-C178-4F9A-99B1-C7E35342D8BC} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F} From f87462e6f28630a50c6cebfb1baa17052350e901 Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 07:44:49 -0600 Subject: [PATCH 3/6] added cipher seed for testing --- util/DBSeeder/EFDBSeeder.cs | 113 +++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 34 deletions(-) diff --git a/util/DBSeeder/EFDBSeeder.cs b/util/DBSeeder/EFDBSeeder.cs index 1d165f3c193b..119f43afd4b3 100644 --- a/util/DBSeeder/EFDBSeeder.cs +++ b/util/DBSeeder/EFDBSeeder.cs @@ -1,14 +1,11 @@ -using System; -using System.Linq; -using System.Data; -using System.Reflection; -using System.Text; -using Bit.Core; -using Bit.Infrastructure.EntityFramework.Models; // Change this line +using System.Text.Json; +using Bit.Core.Vault.Enums; // Change this line using Bit.Infrastructure.EntityFramework.Repositories; -using Microsoft.EntityFrameworkCore; +using Bit.Infrastructure.EntityFramework.Vault.Models; using Microsoft.Extensions.Logging; + + namespace Bit.DBSeeder; public class EFDBSeeder @@ -21,16 +18,16 @@ public EFDBSeeder(string connectionString, string databaseProvider) { _connectionString = connectionString; _databaseProvider = databaseProvider; - + } public bool SeedDatabase() { - //print connectionstring to console + //print connectionstring to console Console.WriteLine(_connectionString); Console.WriteLine(_databaseProvider); - - try + + try { var factory = new DatabaseContextFactory(); using (var context = factory.CreateDbContext(new[] { _connectionString })) @@ -38,10 +35,11 @@ public bool SeedDatabase() if (context.Database.CanConnect()) { Console.WriteLine("Successfully connected to the database!"); - + // Seed the database SeedUsers(context); - + SeedCipher(context); + Console.WriteLine("Database seeded successfully!"); } else @@ -56,9 +54,9 @@ public bool SeedDatabase() Console.WriteLine($"An error occurred: {ex.Message}"); return false; } - - - + + + return true; } @@ -68,13 +66,13 @@ private void SeedUsers(DatabaseContext context) { context.Users.AddRange( new Bit.Infrastructure.EntityFramework.Models.User // Specify the full namespace - { - Id = Guid.NewGuid(), - Name = "Test User", - Email = "testuser@example.com", - EmailVerified = true, - SecurityStamp = Guid.NewGuid().ToString(), - ApiKey = "TestApiKey" + { + Id = Guid.NewGuid(), + Name = "Test User", + Email = "testuser@example.com", + EmailVerified = true, + SecurityStamp = Guid.NewGuid().ToString(), + ApiKey = "TestApiKey" } ); context.SaveChanges(); @@ -86,19 +84,66 @@ private void SeedUsers(DatabaseContext context) } } - /* private ILogger CreateLogger() + private void SeedCipher(DatabaseContext context) { - var loggerFactory = LoggerFactory.Create(builder => + if (!context.Ciphers.Any()) { - builder - .AddFilter("Microsoft", LogLevel.Warning) - .AddFilter("System", LogLevel.Warning) - .AddConsole(); + var testUser = context.Users.FirstOrDefault(); + if (testUser == null) + { + Console.WriteLine("No users found. Please seed users first."); + return; + } - builder.AddFilter("EFDBSeeder.EFDBSeeder", LogLevel.Information); - }); + var cipher = new Cipher + { + Id = Guid.NewGuid(), + UserId = testUser.Id, + OrganizationId = null, // Set this if needed + Type = CipherType.Login, + Data = JsonSerializer.Serialize(new + { + Name = "Test Login", + Notes = "This is a test login cipher", + Login = new + { + Username = "testuser", + Password = "testpassword", + Uri = "https://example.com" + } + }), + Favorites = null, + Folders = null, + Attachments = null, + CreationDate = DateTime.UtcNow, + RevisionDate = DateTime.UtcNow, + DeletedDate = null, + Reprompt = CipherRepromptType.None + }; - return loggerFactory.CreateLogger(); + context.Ciphers.Add(cipher); + context.SaveChanges(); + Console.WriteLine("Test cipher added to the database."); + } + else + { + Console.WriteLine("Ciphers table is not empty. Skipping cipher seeding."); + } } - */ + + /* private ILogger CreateLogger() + { + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .AddFilter("Microsoft", LogLevel.Warning) + .AddFilter("System", LogLevel.Warning) + .AddConsole(); + + builder.AddFilter("EFDBSeeder.EFDBSeeder", LogLevel.Information); + }); + + return loggerFactory.CreateLogger(); + } + */ } From e9b4d09020fb2a5a827c71a02a4f44fe79d582bf Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 07:58:02 -0600 Subject: [PATCH 4/6] fix include directive for factories.cs --- util/DBSeeder/Factories.cs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/util/DBSeeder/Factories.cs b/util/DBSeeder/Factories.cs index 2e723ab40706..64caebd01086 100644 --- a/util/DBSeeder/Factories.cs +++ b/util/DBSeeder/Factories.cs @@ -1,30 +1,28 @@ -using Bit.Core.Settings; -using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace Bit.DBSeeder; - public class DatabaseContextFactory : IDesignTimeDbContextFactory +public class DatabaseContextFactory : IDesignTimeDbContextFactory +{ + public DatabaseContext CreateDbContext(string[] args) { - public DatabaseContext CreateDbContext(string[] args) + if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0])) { - if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0])) - { - throw new ArgumentException("Connection string must be provided as the first argument."); - } + throw new ArgumentException("Connection string must be provided as the first argument."); + } - var connectionString = args[0]; + var connectionString = args[0]; - var services = new ServiceCollection(); - services.AddDataProtection(); - services.AddDbContext(options => - options.UseSqlServer(connectionString)); - var serviceProvider = services.BuildServiceProvider(); + var services = new ServiceCollection(); + services.AddDataProtection(); + services.AddDbContext(options => + options.UseSqlServer(connectionString)); + var serviceProvider = services.BuildServiceProvider(); - return serviceProvider.GetRequiredService(); - } + return serviceProvider.GetRequiredService(); } +} From 6b03d9f2ed4d1167253b7afcba76969d835e17aa Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 08:10:54 -0600 Subject: [PATCH 5/6] fix formatting --- util/EFDBSeederUtility/Program.cs | 36 +++++++++++-------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/util/EFDBSeederUtility/Program.cs b/util/EFDBSeederUtility/Program.cs index fc50e00697ed..60c08f756396 100644 --- a/util/EFDBSeederUtility/Program.cs +++ b/util/EFDBSeederUtility/Program.cs @@ -10,30 +10,20 @@ private static int Main(string[] args) [DefaultCommand] public void Execute( - - - - - - - [Operand(Description = "Database provider (mssql, mysql, postgres, sqlite).")] string databaseProvider, - [Operand(Description = "Database connection string.")] string ConnectionString - - - - - - ) => SeedDatabase(ConnectionString, databaseProvider); + + [Operand(Description = "Database provider (mssql, mysql, postgres, sqlite).")] string databaseProvider, + [Operand(Description = "Database connection string.")] string ConnectionString + + ) => SeedDatabase(ConnectionString, databaseProvider); private static bool SeedDatabase(string databaseConnectionString, - string databaseProvider) -{ - var seeder = new EFDBSeeder(databaseConnectionString, databaseProvider); - bool success; - - success = seeder.SeedDatabase(); // Change this line - + string databaseProvider) + { + var seeder = new EFDBSeeder(databaseConnectionString, databaseProvider); + bool success; + + success = seeder.SeedDatabase(); // Change this line - return success; + return success; + } } -} \ No newline at end of file From 82201c00eba9750d646b4da6c31832342a1e9ea2 Mon Sep 17 00:00:00 2001 From: Robert Y Date: Wed, 3 Jul 2024 08:49:54 -0600 Subject: [PATCH 6/6] Add rudimentary auto generation for 5000 users and ciphers --- util/DBSeeder/EFDBSeeder.cs | 120 +++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 42 deletions(-) diff --git a/util/DBSeeder/EFDBSeeder.cs b/util/DBSeeder/EFDBSeeder.cs index 119f43afd4b3..ecad691edc95 100644 --- a/util/DBSeeder/EFDBSeeder.cs +++ b/util/DBSeeder/EFDBSeeder.cs @@ -2,6 +2,7 @@ using Bit.Core.Vault.Enums; // Change this line using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Vault.Models; +using Bogus; using Microsoft.Extensions.Logging; @@ -38,7 +39,7 @@ public bool SeedDatabase() // Seed the database SeedUsers(context); - SeedCipher(context); + SeedCiphers(context); Console.WriteLine("Database seeded successfully!"); } @@ -51,8 +52,12 @@ public bool SeedDatabase() } catch (Exception ex) { - Console.WriteLine($"An error occurred: {ex.Message}"); - return false; + Console.WriteLine($"Error adding users: {ex.Message}"); + if (ex.InnerException != null) + { + Console.WriteLine($"Inner exception: {ex.InnerException.Message}"); + } + throw; // Re-throw the exception to stop the seeding process } @@ -64,19 +69,29 @@ private void SeedUsers(DatabaseContext context) { if (!context.Users.Any()) { - context.Users.AddRange( - new Bit.Infrastructure.EntityFramework.Models.User // Specify the full namespace + Console.WriteLine("Generating 5000 users..."); + + var faker = new Faker() + .RuleFor(u => u.Id, f => Guid.NewGuid()) + .RuleFor(u => u.Name, f => f.Name.FullName()) + .RuleFor(u => u.Email, (f, u) => f.Internet.Email(u.Name)) + .RuleFor(u => u.EmailVerified, f => f.Random.Bool(0.9f)) + .RuleFor(u => u.SecurityStamp, f => Guid.NewGuid().ToString()) + .RuleFor(u => u.ApiKey, f => Guid.NewGuid().ToString("N").Substring(0, 30)) + .RuleFor(u => u.CreationDate, f => f.Date.Past(2)) + .RuleFor(u => u.RevisionDate, (f, u) => f.Date.Between(u.CreationDate, DateTime.UtcNow)); + + var users = faker.Generate(5000); + + const int batchSize = 100; + for (int i = 0; i < users.Count; i += batchSize) { - Id = Guid.NewGuid(), - Name = "Test User", - Email = "testuser@example.com", - EmailVerified = true, - SecurityStamp = Guid.NewGuid().ToString(), - ApiKey = "TestApiKey" + context.Users.AddRange(users.Skip(i).Take(batchSize)); + context.SaveChanges(); + Console.WriteLine($"Added {Math.Min(i + batchSize, users.Count)} users"); } - ); - context.SaveChanges(); - Console.WriteLine("Test user added to the database."); + + Console.WriteLine("5000 test users added to the database."); } else { @@ -84,46 +99,67 @@ private void SeedUsers(DatabaseContext context) } } - private void SeedCipher(DatabaseContext context) + private void SeedCiphers(DatabaseContext context) { if (!context.Ciphers.Any()) { - var testUser = context.Users.FirstOrDefault(); - if (testUser == null) + var users = context.Users.ToList(); + if (!users.Any()) { Console.WriteLine("No users found. Please seed users first."); return; } - var cipher = new Cipher - { - Id = Guid.NewGuid(), - UserId = testUser.Id, - OrganizationId = null, // Set this if needed - Type = CipherType.Login, - Data = JsonSerializer.Serialize(new + Console.WriteLine($"Generating ciphers for {users.Count} users..."); + + var faker = new Faker() + .RuleFor(c => c.Id, f => Guid.NewGuid()) + .RuleFor(c => c.Type, f => CipherType.Login) + .RuleFor(c => c.Data, f => JsonSerializer.Serialize(new { - Name = "Test Login", - Notes = "This is a test login cipher", + Name = f.Internet.DomainName(), + Notes = f.Lorem.Sentence(), Login = new { - Username = "testuser", - Password = "testpassword", - Uri = "https://example.com" + Username = f.Internet.UserName(), + Password = f.Internet.Password(), + Uri = f.Internet.Url() + } + })) + .RuleFor(c => c.CreationDate, f => f.Date.Past(1)) + .RuleFor(c => c.RevisionDate, (f, c) => f.Date.Between(c.CreationDate, DateTime.UtcNow)) + .RuleFor(c => c.DeletedDate, f => null) + .RuleFor(c => c.Reprompt, f => CipherRepromptType.None); + + const int batchSize = 100; + for (int i = 0; i < users.Count; i += batchSize) + { + var userBatch = users.Skip(i).Take(batchSize); + var ciphers = userBatch.Select(user => + { + var cipher = faker.Generate(); + cipher.UserId = user.Id; + return cipher; + }).ToList(); + + try + { + context.Ciphers.AddRange(ciphers); + context.SaveChanges(); + Console.WriteLine($"Added ciphers for users {i + 1} to {Math.Min(i + batchSize, users.Count)}"); + } + catch (Exception ex) + { + Console.WriteLine($"Error adding ciphers: {ex.Message}"); + if (ex.InnerException != null) + { + Console.WriteLine($"Inner exception: {ex.InnerException.Message}"); } - }), - Favorites = null, - Folders = null, - Attachments = null, - CreationDate = DateTime.UtcNow, - RevisionDate = DateTime.UtcNow, - DeletedDate = null, - Reprompt = CipherRepromptType.None - }; - - context.Ciphers.Add(cipher); - context.SaveChanges(); - Console.WriteLine("Test cipher added to the database."); + throw; // Re-throw the exception to stop the seeding process + } + } + + Console.WriteLine($"Ciphers added for all {users.Count} users."); } else {