1
0
mirror of https://github.com/bitwarden/server synced 2026-02-08 04:33:23 +00:00

Simplify by using arguments model

This commit is contained in:
Mick Letofsky
2026-01-27 18:34:31 +01:00
parent 30bc4c76c3
commit 014e930147
2 changed files with 105 additions and 84 deletions

View File

@@ -2,8 +2,6 @@
using Bit.Core.Entities;
using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.RustSDK;
using Bit.Seeder.Data.Enums;
using Bit.Seeder.Options;
using Bit.Seeder.Recipes;
using CommandDotNet;
using Microsoft.AspNetCore.Identity;
@@ -44,42 +42,9 @@ public class Program
}
[Command("vault-organization", Description = "Seed an organization with users and encrypted vault data (ciphers, collections, groups)")]
public void VaultOrganization(
[Option('n', "name", Description = "Name of organization")]
string name,
[Option('u', "users", Description = "Number of users to generate (minimum 1)")]
int users,
[Option('d', "domain", Description = "Email domain for users")]
string domain,
[Option('c', "ciphers", Description = "Number of login ciphers to create (required, minimum 1)")]
int ciphers,
[Option('g', "groups", Description = "Number of groups to create (required, minimum 1)")]
int groups,
[Option('m', "mix-user-statuses", Description = "Use realistic status mix (85% confirmed, 5% each invited/accepted/revoked). Requires >= 10 users.")]
bool mixStatuses = true,
[Option('o', "org-structure", Description = "Org structure for collections: Traditional, Spotify, or Modern")]
string? structure = null,
[Option('r', "region", Description = "Geographic region for names: NorthAmerica, Europe, AsiaPacific, LatinAmerica, MiddleEast, Africa, or Global")]
string? region = null
)
public void VaultOrganization(VaultOrganizationArgs args)
{
if (users < 1)
{
throw new ArgumentException("Users must be at least 1. Use another command for orgs without users.");
}
if (ciphers < 1)
{
throw new ArgumentException("Ciphers must be at least 1. Use another command for orgs without vault data.");
}
if (groups < 1)
{
throw new ArgumentException("Groups must be at least 1. Use another command for orgs without groups.");
}
var structureModel = ParseOrgStructure(structure);
var geographicRegion = ParseGeographicRegion(region);
args.Validate();
var services = new ServiceCollection();
ServiceCollectionExtension.ConfigureServices(services);
@@ -94,52 +59,6 @@ public class Program
scopedServices.GetRequiredService<RustSdkService>(),
scopedServices.GetRequiredService<IPasswordHasher<User>>());
recipe.Seed(new OrganizationVaultOptions
{
Name = name,
Domain = domain,
Users = users,
Ciphers = ciphers,
Groups = groups,
RealisticStatusMix = mixStatuses,
StructureModel = structureModel,
Region = geographicRegion
});
}
private static OrgStructureModel? ParseOrgStructure(string? structure)
{
if (string.IsNullOrEmpty(structure))
{
return null;
}
return structure.ToLowerInvariant() switch
{
"traditional" => OrgStructureModel.Traditional,
"spotify" => OrgStructureModel.Spotify,
"modern" => OrgStructureModel.Modern,
_ => throw new ArgumentException($"Unknown structure '{structure}'. Use: Traditional, Spotify, or Modern")
};
}
private static GeographicRegion? ParseGeographicRegion(string? region)
{
if (string.IsNullOrEmpty(region))
{
return null;
}
return region.ToLowerInvariant() switch
{
"northamerica" => GeographicRegion.NorthAmerica,
"europe" => GeographicRegion.Europe,
"asiapacific" => GeographicRegion.AsiaPacific,
"latinamerica" => GeographicRegion.LatinAmerica,
"middleeast" => GeographicRegion.MiddleEast,
"africa" => GeographicRegion.Africa,
"global" => GeographicRegion.Global,
_ => throw new ArgumentException($"Unknown region '{region}'. Use: NorthAmerica, Europe, AsiaPacific, LatinAmerica, MiddleEast, Africa, or Global")
};
recipe.Seed(args.ToOptions());
}
}

View File

@@ -0,0 +1,102 @@
using Bit.Seeder.Data.Enums;
using Bit.Seeder.Options;
using CommandDotNet;
namespace Bit.DbSeederUtility;
/// <summary>
/// CLI argument model for the vault-organization command.
/// Maps to <see cref="OrganizationVaultOptions"/> for the Seeder library.
/// </summary>
public class VaultOrganizationArgs : IArgumentModel
{
[Option('n', "name", Description = "Name of organization")]
public string Name { get; set; } = null!;
[Option('u', "users", Description = "Number of users to generate (minimum 1)")]
public int Users { get; set; }
[Option('d', "domain", Description = "Email domain for users")]
public string Domain { get; set; } = null!;
[Option('c', "ciphers", Description = "Number of login ciphers to create (minimum 1)")]
public int Ciphers { get; set; }
[Option('g', "groups", Description = "Number of groups to create (minimum 1)")]
public int Groups { get; set; }
[Option('m', "mix-user-statuses", Description = "Use realistic status mix (85% confirmed, 5% each invited/accepted/revoked). Requires >= 10 users.")]
public bool MixStatuses { get; set; } = true;
[Option('o', "org-structure", Description = "Org structure for collections: Traditional, Spotify, or Modern")]
public string? Structure { get; set; }
[Option('r', "region", Description = "Geographic region for names: NorthAmerica, Europe, AsiaPacific, LatinAmerica, MiddleEast, Africa, or Global")]
public string? Region { get; set; }
public void Validate()
{
if (Users < 1)
{
throw new ArgumentException("Users must be at least 1. Use another command for orgs without users.");
}
if (Ciphers < 1)
{
throw new ArgumentException("Ciphers must be at least 1. Use another command for orgs without vault data.");
}
if (Groups < 1)
{
throw new ArgumentException("Groups must be at least 1. Use another command for orgs without groups.");
}
}
public OrganizationVaultOptions ToOptions() => new()
{
Name = Name,
Domain = Domain,
Users = Users,
Ciphers = Ciphers,
Groups = Groups,
RealisticStatusMix = MixStatuses,
StructureModel = ParseOrgStructure(Structure),
Region = ParseGeographicRegion(Region)
};
private static OrgStructureModel? ParseOrgStructure(string? structure)
{
if (string.IsNullOrEmpty(structure))
{
return null;
}
return structure.ToLowerInvariant() switch
{
"traditional" => OrgStructureModel.Traditional,
"spotify" => OrgStructureModel.Spotify,
"modern" => OrgStructureModel.Modern,
_ => throw new ArgumentException($"Unknown structure '{structure}'. Use: Traditional, Spotify, or Modern")
};
}
private static GeographicRegion? ParseGeographicRegion(string? region)
{
if (string.IsNullOrEmpty(region))
{
return null;
}
return region.ToLowerInvariant() switch
{
"northamerica" => GeographicRegion.NorthAmerica,
"europe" => GeographicRegion.Europe,
"asiapacific" => GeographicRegion.AsiaPacific,
"latinamerica" => GeographicRegion.LatinAmerica,
"middleeast" => GeographicRegion.MiddleEast,
"africa" => GeographicRegion.Africa,
"global" => GeographicRegion.Global,
_ => throw new ArgumentException($"Unknown region '{region}'. Use: NorthAmerica, Europe, AsiaPacific, LatinAmerica, MiddleEast, Africa, or Global")
};
}
}