Skip to content

Commit 81120bd

Browse files
authored
Enhance seeder allowing for a user-defined password (#7021)
1 parent 3ed9be1 commit 81120bd

14 files changed

Lines changed: 78 additions & 85 deletions

util/DbSeederUtility/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public void Seed(SeedArgs args)
113113
var stopwatch = Stopwatch.StartNew();
114114

115115
Console.WriteLine($"Seeding organization from preset '{args.Preset}'...");
116-
var result = recipe.Seed(args.Preset!);
116+
var result = recipe.Seed(args.Preset!, args.Password);
117117

118118
stopwatch.Stop();
119119
PrintSeedResult(result, stopwatch.Elapsed);

util/DbSeederUtility/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ dotnet build
1111
dotnet run -- <command> [options]
1212
```
1313

14-
**Login Credentials:** All seeded users use password `asdfasdfasdf`. The owner email is `owner@<domain>`.
14+
**Login Credentials:** All seeded users use password `asdfasdfasdf` by default (override with `--password`). The owner email is `owner@<domain>`.
1515

1616
## Commands
1717

@@ -29,6 +29,8 @@ dotnet run -- seed --preset dunder-mifflin-full --mangle
2929

3030
# Large enterprise preset for performance testing
3131
dotnet run -- seed --preset large-enterprise
32+
33+
dotnet run -- seed --preset dunder-mifflin-full --password "MyTestPassword1" --mangle
3234
```
3335

3436
### `organization` - Users Only (No Vault Data)
@@ -65,4 +67,7 @@ dotnet run -- vault-organization -n ApacOrg -d apac.test -u 17 -c 600 -g 12 --re
6567

6668
# With ID mangling for test isolation (prevents collisions with existing data)
6769
dotnet run -- vault-organization -n IsolatedOrg -d isolated.test -u 5 -c 25 -g 4 -o Spotify --mangle
70+
71+
# With custom password for all accounts
72+
dotnet run -- vault-organization -n CustomPwOrg -d custom-password-02.test -u 10 -c 100 -g 3 --password "MyTestPassword1"
6873
```

util/DbSeederUtility/SeedArgs.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Bit.DbSeederUtility;
88
/// </summary>
99
public class SeedArgs : IArgumentModel
1010
{
11-
[Option('p', "preset", Description = "Name of embedded preset to load (e.g., 'dunder-mifflin-full')")]
11+
[Option("preset", Description = "Name of embedded preset to load")]
1212
public string? Preset { get; set; }
1313

1414
[Option('l', "list", Description = "List all available presets and fixtures")]
@@ -17,6 +17,9 @@ public class SeedArgs : IArgumentModel
1717
[Option("mangle", Description = "Enable mangling for test isolation")]
1818
public bool Mangle { get; set; }
1919

20+
[Option("password", Description = "Password for all seeded accounts (default: asdfasdfasdf)")]
21+
public string? Password { get; set; }
22+
2023
public void Validate()
2124
{
2225
// List mode is standalone

util/DbSeederUtility/VaultOrganizationArgs.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ public class VaultOrganizationArgs : IArgumentModel
3737
[Option("mangle", Description = "Enable mangling for test isolation")]
3838
public bool Mangle { get; set; } = false;
3939

40+
[Option("password", Description = "Password for all seeded accounts (default: asdfasdfasdf)")]
41+
public string? Password { get; set; }
42+
4043
public void Validate()
4144
{
4245
if (Users < 1)
@@ -74,7 +77,8 @@ public void Validate()
7477
Groups = Groups,
7578
RealisticStatusMix = MixStatuses,
7679
StructureModel = ParseOrgStructure(Structure),
77-
Region = ParseGeographicRegion(Region)
80+
Region = ParseGeographicRegion(Region),
81+
Password = Password
7882
};
7983

8084
private static OrgStructureModel? ParseOrgStructure(string? structure)

util/Seeder/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,6 @@ _seed = options.Seed ?? StableHash.ToInt32(options.Domain);
9898

9999
## Security Reminders
100100

101-
- Test password: `asdfasdfasdf`
101+
- Default test password: `asdfasdfasdf` (overridable via `--password` CLI flag or `SeederSettings`)
102102
- Never commit database dumps with seeded data
103103
- Seeded keys are for testing only

util/Seeder/Options/OrganizationVaultOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,9 @@ public class OrganizationVaultOptions
8383
/// Seed for deterministic data generation. When null, derived from Domain hash.
8484
/// </summary>
8585
public int? Seed { get; init; }
86+
87+
/// <summary>
88+
/// Password for all seeded accounts. Defaults to "asdfasdfasdf" if not specified.
89+
/// </summary>
90+
public string? Password { get; init; }
8691
}

util/Seeder/Pipeline/PresetExecutor.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,21 @@ internal sealed class PresetExecutor(DatabaseContext db, IMapper mapper)
1818
/// <param name="presetName">Name of the embedded preset (e.g., "dunder-mifflin-full")</param>
1919
/// <param name="passwordHasher">Password hasher for user creation</param>
2020
/// <param name="manglerService">Mangler service for test isolation</param>
21+
/// <param name="password">Optional password for all seeded accounts</param>
2122
/// <returns>Execution result with organization ID and entity counts</returns>
2223
internal ExecutionResult Execute(
2324
string presetName,
2425
IPasswordHasher<User> passwordHasher,
25-
IManglerService manglerService)
26+
IManglerService manglerService,
27+
string? password = null)
2628
{
2729
var reader = new SeedReader();
2830

2931
var services = new ServiceCollection();
3032
services.AddSingleton(passwordHasher);
3133
services.AddSingleton(manglerService);
3234
services.AddSingleton<ISeedReader>(reader);
35+
services.AddSingleton(new SeederSettings(password));
3336
services.AddSingleton(db);
3437

3538
PresetLoader.RegisterRecipe(presetName, reader, services);

util/Seeder/Pipeline/SeederContextExtensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,15 @@ internal static IManglerService GetMangler(this SeederContext context) =>
1919

2020
internal static ISeedReader GetSeedReader(this SeederContext context) =>
2121
context.Services.GetRequiredService<ISeedReader>();
22+
23+
internal static SeederSettings GetSettings(this SeederContext context) =>
24+
context.Services.GetRequiredService<SeederSettings>();
25+
26+
internal static string GetPassword(this SeederContext context) =>
27+
context.GetSettings().Password ?? Factories.UserSeeder.DefaultPassword;
2228
}
29+
30+
/// <summary>
31+
/// Runtime settings for a seeding operation, registered in DI.
32+
/// </summary>
33+
internal sealed record SeederSettings(string? Password = null);

util/Seeder/Recipes/OrganizationFromPresetRecipe.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ public class OrganizationFromPresetRecipe(
2727
/// Seeds an organization from an embedded preset.
2828
/// </summary>
2929
/// <param name="presetName">Name of the embedded preset (e.g., "dunder-mifflin-full")</param>
30+
/// <param name="password">Optional password for all seeded accounts</param>
3031
/// <returns>The organization ID and summary statistics.</returns>
31-
public SeedResult Seed(string presetName)
32+
public SeedResult Seed(string presetName, string? password = null)
3233
{
33-
var result = _executor.Execute(presetName, passwordHasher, manglerService);
34+
var result = _executor.Execute(presetName, passwordHasher, manglerService, password);
3435

3536
return new SeedResult(
3637
result.OrganizationId,

util/Seeder/Recipes/OrganizationWithVaultRecipe.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ private record UserWithKey(User User, string SymmetricKey);
5454
public Guid Seed(OrganizationVaultOptions options)
5555
{
5656
_ctx = GeneratorContext.FromOptions(options);
57+
var password = options.Password ?? UserSeeder.DefaultPassword;
5758

5859
var seats = Math.Max(options.Users + 1, _minimumOrgSeats);
5960
var orgKeys = RustSdkService.GenerateOrganizationKeys();
@@ -63,7 +64,11 @@ public Guid Seed(OrganizationVaultOptions options)
6364
options.Name, options.Domain, seats, orgKeys.PublicKey, orgKeys.PrivateKey);
6465

6566
// Create owner user via factory
66-
var ownerUser = UserSeeder.Create($"owner@{options.Domain}", passwordHasher, manglerService);
67+
var ownerEmail = $"owner@{options.Domain}";
68+
var mangledOwnerEmail = manglerService.Mangle(ownerEmail);
69+
var ownerKeys = RustSdkService.GenerateUserKeys(mangledOwnerEmail, password);
70+
var ownerUser = UserSeeder.Create(mangledOwnerEmail, passwordHasher, manglerService, keys: ownerKeys, password: password);
71+
6772
var ownerOrgKey = RustSdkService.GenerateUserOrganizationKey(ownerUser.PublicKey!, orgKeys.Key);
6873
var ownerOrgUser = organization.CreateOrganizationUserWithKey(
6974
ownerUser, OrganizationUserType.Owner, OrganizationUserStatusType.Confirmed, ownerOrgKey);
@@ -77,8 +82,8 @@ public Guid Seed(OrganizationVaultOptions options)
7782
{
7883
var email = $"user{i}@{options.Domain}";
7984
var mangledEmail = manglerService.Mangle(email);
80-
var userKeys = RustSdkService.GenerateUserKeys(mangledEmail, UserSeeder.DefaultPassword);
81-
var memberUser = UserSeeder.Create(mangledEmail, passwordHasher, manglerService, keys: userKeys);
85+
var userKeys = RustSdkService.GenerateUserKeys(mangledEmail, password);
86+
var memberUser = UserSeeder.Create(mangledEmail, passwordHasher, manglerService, keys: userKeys, password: password);
8287
memberUsersWithKeys.Add(new UserWithKey(memberUser, userKeys.Key));
8388

8489
var status = useRealisticMix

0 commit comments

Comments
 (0)