From 014e9301477507ddbe6156d7ce7cbcf262b72a5d Mon Sep 17 00:00:00 2001 From: Mick Letofsky Date: Tue, 27 Jan 2026 18:34:31 +0100 Subject: [PATCH] Simplify by using arguments model --- util/DbSeederUtility/Program.cs | 87 +-------------- util/DbSeederUtility/VaultOrganizationArgs.cs | 102 ++++++++++++++++++ 2 files changed, 105 insertions(+), 84 deletions(-) create mode 100644 util/DbSeederUtility/VaultOrganizationArgs.cs diff --git a/util/DbSeederUtility/Program.cs b/util/DbSeederUtility/Program.cs index 6e27170196..1336268de1 100644 --- a/util/DbSeederUtility/Program.cs +++ b/util/DbSeederUtility/Program.cs @@ -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(), scopedServices.GetRequiredService>()); - 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()); } } diff --git a/util/DbSeederUtility/VaultOrganizationArgs.cs b/util/DbSeederUtility/VaultOrganizationArgs.cs new file mode 100644 index 0000000000..0e7335ae14 --- /dev/null +++ b/util/DbSeederUtility/VaultOrganizationArgs.cs @@ -0,0 +1,102 @@ +using Bit.Seeder.Data.Enums; +using Bit.Seeder.Options; +using CommandDotNet; + +namespace Bit.DbSeederUtility; + +/// +/// CLI argument model for the vault-organization command. +/// Maps to for the Seeder library. +/// +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") + }; + } +}