mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 13:38:13 -05:00
deleted backup files
This commit is contained in:
parent
7877042c32
commit
44f4efffaf
@ -1,15 +1,14 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Seeder.Services;
|
||||
using Bit.Seeder.Settings;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Bit.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Bit.Core.Enums;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Seeder.Commands;
|
||||
|
||||
namespace Bit.Seeder.Commands
|
||||
{
|
||||
public class ExtractCommand
|
||||
{
|
||||
public bool Execute(string seedName)
|
||||
@ -116,4 +115,3 @@ namespace Bit.Seeder.Commands
|
||||
services.AddTransient<IEncryptionService, EncryptionService>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Bit.Seeder.Services;
|
||||
using Bit.Seeder.Services;
|
||||
using Bit.Seeder.Settings;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Bit.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Seeder.Commands;
|
||||
|
||||
namespace Bit.Seeder.Commands
|
||||
{
|
||||
public class GenerateCommand
|
||||
{
|
||||
public bool Execute(int users, int ciphersPerUser, string seedName, bool loadImmediately = false)
|
||||
@ -74,4 +73,3 @@ namespace Bit.Seeder.Commands
|
||||
services.AddDbContext<DatabaseContext>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Bit.Seeder.Services;
|
||||
using Bit.Seeder.Services;
|
||||
using Bit.Seeder.Settings;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Bit.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Seeder.Commands;
|
||||
|
||||
namespace Bit.Seeder.Commands
|
||||
{
|
||||
public class LoadCommand
|
||||
{
|
||||
public bool Execute(string seedName, string? timestamp = null, bool dryRun = false)
|
||||
@ -84,4 +83,3 @@ namespace Bit.Seeder.Commands
|
||||
services.AddDbContext<DatabaseContext>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<key id="c9676c44-16a7-4b38-b750-4ea5443f1b87" version="1">
|
||||
<creationDate>2025-03-13T17:13:50.8174933Z</creationDate>
|
||||
<activationDate>2025-03-13T17:13:50.8130955Z</activationDate>
|
||||
<expirationDate>2025-06-11T17:13:50.8130955Z</expirationDate>
|
||||
<descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
|
||||
<descriptor>
|
||||
<encryption algorithm="AES_256_CBC" />
|
||||
<validation algorithm="HMACSHA256" />
|
||||
<masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
|
||||
<!-- Warning: the key below is in an unencrypted form. -->
|
||||
<value>VCxxS1xKEWQ9+XRXgMchHML7POXcwRkCHswj7JMjjN36RkERENo+ky/1mazB4RZ6BjjwXsAyjhSz2eGts/0BnQ==</value>
|
||||
</masterKey>
|
||||
</descriptor>
|
||||
</descriptor>
|
||||
</key>
|
@ -1,8 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Seeder.Settings;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Bit.Core;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
@ -1,11 +0,0 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
public interface IDatabaseService
|
||||
{
|
||||
Task ClearDatabaseAsync();
|
||||
Task SaveUsersAsync(IEnumerable<User> users);
|
||||
Task SaveCiphersAsync(IEnumerable<Cipher> ciphers);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Bit.Seeder.Services;
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
public interface IEncryptionService
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Bit.Seeder.Services;
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
public interface ISeederService
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
|
@ -1,383 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Enums;
|
||||
using Bogus;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Seeder.Services;
|
||||
|
||||
public class SeederService : ISeederService
|
||||
{
|
||||
private readonly IEncryptionService _encryptionService;
|
||||
private readonly IDatabaseService _databaseService;
|
||||
private readonly ILogger<SeederService> _logger;
|
||||
private readonly Faker _faker;
|
||||
private readonly string _defaultPassword = "password";
|
||||
|
||||
public SeederService(
|
||||
IEncryptionService encryptionService,
|
||||
IDatabaseService databaseService,
|
||||
ILogger<SeederService> logger)
|
||||
{
|
||||
_encryptionService = encryptionService;
|
||||
_databaseService = databaseService;
|
||||
_logger = logger;
|
||||
_faker = new Faker();
|
||||
|
||||
// Set the random seed to ensure reproducible data
|
||||
Randomizer.Seed = new Random(42);
|
||||
}
|
||||
|
||||
public async Task GenerateSeedsAsync(int userCount, int ciphersPerUser, string outputName)
|
||||
{
|
||||
_logger.LogInformation("Generating seeds: {UserCount} users with {CiphersPerUser} ciphers each", userCount, ciphersPerUser);
|
||||
|
||||
// Create timestamped folder under a named folder in seeds directory
|
||||
var seedsBaseDir = Path.Combine(Directory.GetCurrentDirectory(), "seeds");
|
||||
Directory.CreateDirectory(seedsBaseDir);
|
||||
|
||||
var namedDir = Path.Combine(seedsBaseDir, outputName);
|
||||
Directory.CreateDirectory(namedDir);
|
||||
|
||||
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
var outputDir = Path.Combine(namedDir, timestamp);
|
||||
Directory.CreateDirectory(outputDir);
|
||||
|
||||
// Create users and ciphers subdirectories
|
||||
Directory.CreateDirectory(Path.Combine(outputDir, "users"));
|
||||
Directory.CreateDirectory(Path.Combine(outputDir, "ciphers"));
|
||||
|
||||
_logger.LogInformation("Seed output directory: {OutputDir}", outputDir);
|
||||
|
||||
// Generate users
|
||||
var users = GenerateUsers(userCount);
|
||||
|
||||
// Generate ciphers for each user
|
||||
var allCiphers = new List<Cipher>();
|
||||
foreach (var user in users)
|
||||
{
|
||||
var ciphers = GenerateCiphers(user, ciphersPerUser);
|
||||
allCiphers.AddRange(ciphers);
|
||||
|
||||
// Save each user's ciphers to a file
|
||||
var cipherFilePath = Path.Combine(outputDir, "ciphers", $"{user.Id}.json");
|
||||
await File.WriteAllTextAsync(cipherFilePath, JsonSerializer.Serialize(ciphers, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
}));
|
||||
}
|
||||
|
||||
// Save users to a file
|
||||
var userFilePath = Path.Combine(outputDir, "users", "users.json");
|
||||
await File.WriteAllTextAsync(userFilePath, JsonSerializer.Serialize(users, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
}));
|
||||
|
||||
_logger.LogInformation("Successfully generated {UserCount} users and {CipherCount} ciphers", users.Count, allCiphers.Count);
|
||||
_logger.LogInformation("Seed data saved to directory: {OutputDir}", outputDir);
|
||||
}
|
||||
|
||||
public async Task GenerateAndLoadSeedsAsync(int userCount, int ciphersPerUser, string seedName)
|
||||
{
|
||||
_logger.LogInformation("Generating and loading seeds directly: {UserCount} users with {CiphersPerUser} ciphers each",
|
||||
userCount, ciphersPerUser);
|
||||
|
||||
// Generate users directly without saving to files
|
||||
var users = GenerateUsers(userCount);
|
||||
|
||||
// Clear the database first
|
||||
await _databaseService.ClearDatabaseAsync();
|
||||
|
||||
// Save users to database
|
||||
await _databaseService.SaveUsersAsync(users);
|
||||
_logger.LogInformation("Saved {UserCount} users directly to database", users.Count);
|
||||
|
||||
// Generate and save ciphers for each user
|
||||
int totalCiphers = 0;
|
||||
foreach (var user in users)
|
||||
{
|
||||
var ciphers = GenerateCiphers(user, ciphersPerUser);
|
||||
await _databaseService.SaveCiphersAsync(ciphers);
|
||||
totalCiphers += ciphers.Count;
|
||||
_logger.LogInformation("Saved {CipherCount} ciphers for user {UserId} directly to database",
|
||||
ciphers.Count, user.Id);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully generated and loaded {UserCount} users and {CipherCount} ciphers directly to database",
|
||||
users.Count, totalCiphers);
|
||||
}
|
||||
|
||||
public async Task LoadSeedsAsync(string seedName, string? timestamp = null)
|
||||
{
|
||||
// Construct path to seeds directory
|
||||
var seedsBaseDir = Path.Combine(Directory.GetCurrentDirectory(), "seeds");
|
||||
var namedDir = Path.Combine(seedsBaseDir, seedName);
|
||||
|
||||
if (!Directory.Exists(namedDir))
|
||||
{
|
||||
_logger.LogError("Seed directory not found: {SeedDir}", namedDir);
|
||||
return;
|
||||
}
|
||||
|
||||
string seedDir;
|
||||
|
||||
// If timestamp is specified, use that exact directory
|
||||
if (!string.IsNullOrEmpty(timestamp))
|
||||
{
|
||||
seedDir = Path.Combine(namedDir, timestamp);
|
||||
if (!Directory.Exists(seedDir))
|
||||
{
|
||||
_logger.LogError("Timestamp directory not found: {TimestampDir}", seedDir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, find the most recent timestamped directory
|
||||
var timestampDirs = Directory.GetDirectories(namedDir);
|
||||
if (timestampDirs.Length == 0)
|
||||
{
|
||||
_logger.LogError("No seed data found in directory: {SeedDir}", namedDir);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort by directory name (which is a timestamp) in descending order
|
||||
Array.Sort(timestampDirs);
|
||||
Array.Reverse(timestampDirs);
|
||||
|
||||
// Use the most recent one
|
||||
seedDir = timestampDirs[0];
|
||||
_logger.LogInformation("Using most recent seed data from: {SeedDir}", seedDir);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loading seeds from directory: {SeedDir}", seedDir);
|
||||
|
||||
// Clear database first
|
||||
await _databaseService.ClearDatabaseAsync();
|
||||
|
||||
// Load users
|
||||
var userFilePath = Path.Combine(seedDir, "users", "users.json");
|
||||
if (!File.Exists(userFilePath))
|
||||
{
|
||||
_logger.LogError("User file not found: {UserFilePath}", userFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
var userJson = await File.ReadAllTextAsync(userFilePath);
|
||||
var users = JsonSerializer.Deserialize<List<User>>(userJson, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
}) ?? new List<User>();
|
||||
|
||||
if (users.Count == 0)
|
||||
{
|
||||
_logger.LogError("No users found in user file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Save users to database
|
||||
await _databaseService.SaveUsersAsync(users);
|
||||
|
||||
// Load and save ciphers for each user
|
||||
var cipherDir = Path.Combine(seedDir, "ciphers");
|
||||
if (!Directory.Exists(cipherDir))
|
||||
{
|
||||
_logger.LogError("Cipher directory not found: {CipherDir}", cipherDir);
|
||||
return;
|
||||
}
|
||||
|
||||
var cipherFiles = Directory.GetFiles(cipherDir, "*.json");
|
||||
foreach (var cipherFile in cipherFiles)
|
||||
{
|
||||
var cipherJson = await File.ReadAllTextAsync(cipherFile);
|
||||
var ciphers = JsonSerializer.Deserialize<List<Cipher>>(cipherJson, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
}) ?? new List<Cipher>();
|
||||
|
||||
if (ciphers.Count > 0)
|
||||
{
|
||||
await _databaseService.SaveCiphersAsync(ciphers);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully loaded seed data into database");
|
||||
}
|
||||
|
||||
private List<User> GenerateUsers(int count)
|
||||
{
|
||||
_logger.LogInformation("Generating {Count} users", count);
|
||||
|
||||
var users = new List<User>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
var email = _faker.Internet.Email(provider: "example.com");
|
||||
var name = _faker.Name.FullName();
|
||||
var masterPassword = _encryptionService.HashPassword(_defaultPassword);
|
||||
var masterPasswordHint = "It's the word 'password'";
|
||||
var key = _encryptionService.DeriveKey(_defaultPassword, email);
|
||||
|
||||
var user = new User
|
||||
{
|
||||
Id = userId,
|
||||
Email = email,
|
||||
Name = name,
|
||||
MasterPassword = masterPassword,
|
||||
MasterPasswordHint = masterPasswordHint,
|
||||
SecurityStamp = Guid.NewGuid().ToString(),
|
||||
EmailVerified = true,
|
||||
ApiKey = Guid.NewGuid().ToString("N").Substring(0, 30),
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = 100000,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Key = _encryptionService.EncryptString(Convert.ToBase64String(key), key)
|
||||
};
|
||||
|
||||
users.Add(user);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
private List<Cipher> GenerateCiphers(User user, int count)
|
||||
{
|
||||
_logger.LogInformation("Generating {Count} ciphers for user {UserId}", count, user.Id);
|
||||
|
||||
var ciphers = new List<Cipher>();
|
||||
var key = _encryptionService.DeriveKey(_defaultPassword, user.Email);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var cipherId = Guid.NewGuid();
|
||||
CipherType type;
|
||||
string name;
|
||||
string? notes = null;
|
||||
|
||||
var typeRandom = _faker.Random.Int(1, 4);
|
||||
type = (CipherType)typeRandom;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
name = $"Login - {_faker.Internet.DomainName()}";
|
||||
var loginData = new
|
||||
{
|
||||
Name = name,
|
||||
Notes = notes,
|
||||
Username = _faker.Internet.UserName(),
|
||||
Password = _faker.Internet.Password(),
|
||||
Uris = new[]
|
||||
{
|
||||
new { Uri = $"https://{_faker.Internet.DomainName()}" }
|
||||
}
|
||||
};
|
||||
|
||||
var loginDataJson = JsonSerializer.Serialize(loginData);
|
||||
|
||||
ciphers.Add(new Cipher
|
||||
{
|
||||
Id = cipherId,
|
||||
UserId = user.Id,
|
||||
Type = type,
|
||||
Data = _encryptionService.EncryptString(loginDataJson, key),
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Reprompt = CipherRepromptType.None
|
||||
});
|
||||
break;
|
||||
|
||||
case CipherType.SecureNote:
|
||||
name = $"Note - {_faker.Lorem.Word()}";
|
||||
notes = _faker.Lorem.Paragraph();
|
||||
var secureNoteData = new
|
||||
{
|
||||
Name = name,
|
||||
Notes = notes,
|
||||
Type = 0 // Text
|
||||
};
|
||||
|
||||
var secureNoteDataJson = JsonSerializer.Serialize(secureNoteData);
|
||||
|
||||
ciphers.Add(new Cipher
|
||||
{
|
||||
Id = cipherId,
|
||||
UserId = user.Id,
|
||||
Type = type,
|
||||
Data = _encryptionService.EncryptString(secureNoteDataJson, key),
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Reprompt = CipherRepromptType.None
|
||||
});
|
||||
break;
|
||||
|
||||
case CipherType.Card:
|
||||
name = $"Card - {_faker.Finance.CreditCardNumber().Substring(0, 4)}";
|
||||
var cardData = new
|
||||
{
|
||||
Name = name,
|
||||
Notes = notes,
|
||||
CardholderName = _faker.Name.FullName(),
|
||||
Number = _faker.Finance.CreditCardNumber(),
|
||||
ExpMonth = _faker.Random.Int(1, 12).ToString(),
|
||||
ExpYear = _faker.Random.Int(DateTime.UtcNow.Year, DateTime.UtcNow.Year + 10).ToString(),
|
||||
Code = _faker.Random.Int(100, 999).ToString()
|
||||
};
|
||||
|
||||
var cardDataJson = JsonSerializer.Serialize(cardData);
|
||||
|
||||
ciphers.Add(new Cipher
|
||||
{
|
||||
Id = cipherId,
|
||||
UserId = user.Id,
|
||||
Type = type,
|
||||
Data = _encryptionService.EncryptString(cardDataJson, key),
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Reprompt = CipherRepromptType.None
|
||||
});
|
||||
break;
|
||||
|
||||
case CipherType.Identity:
|
||||
name = $"Identity - {_faker.Name.FullName()}";
|
||||
var identityData = new
|
||||
{
|
||||
Name = name,
|
||||
Notes = notes,
|
||||
Title = _faker.Name.Prefix(),
|
||||
FirstName = _faker.Name.FirstName(),
|
||||
MiddleName = _faker.Name.FirstName(),
|
||||
LastName = _faker.Name.LastName(),
|
||||
Email = _faker.Internet.Email(),
|
||||
Phone = _faker.Phone.PhoneNumber(),
|
||||
Address1 = _faker.Address.StreetAddress(),
|
||||
City = _faker.Address.City(),
|
||||
State = _faker.Address.State(),
|
||||
PostalCode = _faker.Address.ZipCode(),
|
||||
Country = _faker.Address.CountryCode()
|
||||
};
|
||||
|
||||
var identityDataJson = JsonSerializer.Serialize(identityData);
|
||||
|
||||
ciphers.Add(new Cipher
|
||||
{
|
||||
Id = cipherId,
|
||||
UserId = user.Id,
|
||||
Type = type,
|
||||
Data = _encryptionService.EncryptString(identityDataJson, key),
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Reprompt = CipherRepromptType.None
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ciphers;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Bit.Seeder.Settings;
|
||||
namespace Bit.Seeder.Settings;
|
||||
|
||||
public class GlobalSettings
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Bit.Seeder.Settings;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user