Bitwarden SeederUtility 深度指南:用 CLI 为数据库注入组织、用户与 Stripe 测试订阅
2026/9/13 17:10:36 网站建设 项目流程

Bitwarden SeederUtility 深度指南:用 CLI 为数据库注入组织、用户与 Stripe 测试订阅

【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server

SeederUtility 是 Bitwarden 仓库中util/Seeder库的命令行封装,用于在 Bitwarden 数据库中批量生成可控的测试数据——组织(含用户、分组、集合、密码条)、独立个人用户、以及基于 JSON 预设的可复现场景。读完本文,你将掌握全部三个子命令的完整参数与取值约束、Stripe 测试模式计费的开通前提与失败边界,并能从源码层面理解参数校验、依赖注入延迟初始化等实现细节。

工具定位与运行架构

SeederUtility 基于 CommandDotNet 构建,入口为 Program.cs,其Main方法通过AppRunner<Program>注册了三个[Subcommand]子命令:organizationpresetindividual,分别对应 OrganizationCommand、PresetCommand 与 IndividualCommand。

每个命令的执行路径一致:

  1. 调用args.Validate()完成参数校验(非法参数抛出ArgumentException);
  2. 通过 SeederServiceFactory.Create 构造一个独立的ServiceCollection,把 Seeder 库所需的DatabaseContextIMapperIPasswordHasher<User>IManglerServiceILicensingServiceIAttachmentStorageServiceISeederLicenseSigner等解析进作用域;
  3. 委托给 Seeder 库的 Recipe 执行落库——organization/presetOrganizationRecipe.SeedAsyncindividualIndividualUserRecipe.SeedAsync
  4. ConsoleOutput.PrintRow输出结果键值对(如OrganizationOwnerPassword、各类计数)。

两个值得注意的源码设计:

  • 计费依赖延迟构造:SeederServiceFactory 中BillingInitializer被包成一个Func<IStripeBillingInitializer>闭包,只有真正传了--stripe-billing的命令才会触达IOrganizationBillingService -> IBraintreeGateway, IStripeAdapter, ...这条 DI 图,避免每条命令都构建计费子系统;
  • 统一错误退出码:三个命令都catch (Exception ex) when (ex is ArgumentException or InvalidOperationException),打印Error: ...Environment.Exit(1),因此脚本化调用可以依赖退出码判断成败。

快速开始

util/SeederUtility目录下构建并运行:

dotnet build dotnet run -- <command> [options]

登录凭据约定(后续所有命令通用):

  • 所有被种子化的用户默认密码为asdfasdfasdf,可用--password覆盖;
  • 组织预设的组织者邮箱默认为owner@<domain>,可用--owner-email覆盖;个人预设的邮箱来自预设内的user.email字段;
  • individual命令:提供--first-name/--last-name时邮箱为{first}.{last}@individual.example;不提供姓名时生成随机 Faker 身份,并自动启用 mangling(源码见 IndividualArgs.Validate)。

organization:自由控制组织形态

当预设目录无法满足需求时使用——例如需要一个完全没有库数据的组织(所有预设都包含 ciphers)。命令定义为 OrganizationCommand:“Seed an organization with users and optional vault data (ciphers, collections, groups)”。

常用示例(继承自官方 README)

# 带库数据的小组织 dotnet run -- organization -n SmallOrg -d small.example -u 3 -c 10 -g 5 -o Traditional --mangle # 仅用户,无库数据 dotnet run -- organization -n MyOrgNoCiphers -u 100 -d myorg-no-ciphers.example # 自定义密码与计划类型 dotnet run -- organization -n CustomOrg -d custom.example -u 10 -c 100 -g 3 --password "MyTestPassword1" --plan-type teams-annually

完整参数表(源自 OrganizationArgs.cs)

参数说明与默认值
-n, --name组织名称(必填)
-u, --users用户数,最小 1
-d, --domain用户邮箱域,必须以.example结尾(RFC 2606 保留域)
--claimed-domain已验证(claimed)域,可重复传入多个
-c, --ciphers密码条数量,默认 0(无库数据)
-g, --groups分组数量,默认 0
--collections集合数量,默认 0;使用密度档案时必需
--density密度档案:balancedhighPermhighCollectionbroadminimalgroupHeavysparse
-m, --mix-user-statuses真实状态混合:85% confirmed、invited/accepted/revoked 各 5%;默认 true,需 ≥10 用户
-o, --org-structure集合结构:TraditionalSpotifyModernGovernmentSchoolDistrictHealthcareStartup
-r, --region人名地理区域:NorthAmericaEuropeAsiaPacificLatinAmericaMiddleEastAfricaGlobal
--mangle启用 mangling 做测试隔离(每次运行生成唯一的 ID/邮箱/标识符)
--password所有账户密码,默认asdfasdfasdf
--owner-email覆盖组织者邮箱,必须是合法邮箱且 User 表中不存在
--plan-typefreeteams-monthlyteams-annuallyenterprise-monthlyenterprise-annuallyteams-starterfamilies-annually默认enterprise-annually
--kdf-iterationsKDF 迭代次数,默认 5000,最小 5000;生产级 e2e 测试建议 600000
--auto-confirm-users邀请用户无需人工审批自动确认
--allow-admin-collection-access允许管理员/所有者访问所有集合项
--limit-item-deletion仅 Can Manage 权限成员可删除条目
--limit-collection-creation/--limit-collection-deletion集合创建/删除仅限管理员/所有者
--stripe-billing/--skip-trial/--trial-daysStripe 计费选项,见下文

参数校验规则

OrganizationArgs.Validate 在执行前强制以下约束,违反即报Error:并以退出码 1 终止:

  • Users至少为 1;
  • Domain必须以.example结尾,例如myorg.example
  • --structure--region--density均为闭集枚举,非法值给出允许列表提示;
  • --plan-type必须可被PlanFeatures.Parse解析;
  • --kdf-iterations小于 5000 被拒绝;
  • --stripe-billing--plan-type free互斥——“The Free plan has no Stripe subscription”;
  • --owner-email必须包含@

individual:独立个人用户

用于需要可预测邮箱的命名用户带生成式个人库的场景——individual 预设只会创建无库数据的裸账户。命令见 IndividualCommand。

# 命名用户,可预测邮箱 (john.doe@individual.example) dotnet run -- individual --subscription free --first-name John --last-name Doe # Premium 命名用户,带个人库(约 75 条 ciphers、5 个文件夹) dotnet run -- individual --subscription premium --first-name Jane --last-name Smith --vault # 随机姓名——自动启用 mangling dotnet run -- individual --subscription premium --vault # 自建实例——签署并写入 license 文件,使 premium 状态被识别 dotnet run -- individual --subscription premium --first-name Jane --last-name Smith --self-hosted # 老化账户——CreationDate 回拨 365 天 dotnet run -- individual --subscription free --account-age-days 365

参数要点(源自 IndividualArgs.cs):

  • --subscription仅接受freepremium(大小写不敏感);
  • --first-name/--last-name必须成对出现,否则报错 “Provide both ... or neither”;
  • --vault生成约 75 条个人 ciphers 与文件夹;
  • --kdf-iterations默认 5000、最小 5000;--account-age-days不得为负(默认 0 即当天);
  • --email可显式指定邮箱。

自建实例的 license 机制:加--self-hosted才会为 premium 状态签署并写入 license 文件;只有当licenseCertificatePathlicenseCertificatePassword指向持有 Bitwarden开发版licensing 密钥的 PFX 时才实际写 license——生产证书被刻意不信任。找不到匹配证书时,seeder 记录警告并跳过 license 生成,账户依然创建,只是 premium 状态不被识别。

账户老化的边界--account-age-days N只回拨CreationDate,各修订时间戳(revision dates)仍停留在种子化当时——从源码结构看,这保持了时间线的一致性,避免修订日期早于创建日期的矛盾数据。

preset:基于内嵌 JSON 目录的可复现场景

预设是精选的 JSON fixture,包含特定的用户、分组、集合与 cipher 关系——每次运行都是同一份数据,适合需要已知、可复现场景而非随机生成数据的场合。

# 列出可用预设 dotnet run -- preset --list # 日常开发预设,带易记的角色登录 dotnet run -- preset --name dev.playground # QA 预设,已知用户与关系 dotnet run -- preset --name qa.enterprise-basic --mangle # 性能测试的规模化预设 dotnet run -- preset --name scale.md-balanced-sterling-cooper --mangle # 个人用户预设 dotnet run -- preset --name individual.premium --mangle

命令路由逻辑(PresetCommand.ExecuteAsync):通过PresetCatalogService.IsIndividualPreset判断预设属于个人还是组织,分别走IndividualUserRecipeOrganizationRecipe--list支持--output text|json两种格式,JSON 输出会按organization/individual分组并附Available Fixtures清单。

组织预设接受以下覆盖参数(PresetArgs.cs):

  • --mangle:每次运行生成唯一 ID、邮箱与标识符,使同一预设可反复种子化;
  • --org-name:覆盖组织显示名;
  • --owner-email:覆盖所有者登录邮箱(默认owner@<preset-domain>);
  • --kdf-iterations:覆盖预设中的 KDF 值;
  • --stripe-billing/--skip-trial/--trial-days:与organization命令完全一致的 Stripe 选项。

两个覆盖项可与--mangle组合使用。个人预设不支持--stripe-billing,源码会直接抛出 “Only organization presets can be billed today”(PresetCommand.cs 第 28–33 行)。

完整预设目录见 presets.md;不知道跑什么命令时可先查 Scenarios 指南,它把常见任务映射到具体命令。

Stripe 测试计费:让 Billing 页面真正可用

默认情况下,种子化组织没有任何计费数据:网关字段保持 NULL,seeder 对 Stripe零调用。对组织执行--stripe-billingorganizationpreset)后,会在 Stripe测试环境创建真实 customer 与 subscription,使座位自动扩容、订阅管理与升级流程表现得和手工创建的组织一样——否则 Billing → Subscription 页面会挂起,无从测试。

计费参数

三个选项由 StripeBillingArgs 统一校验与映射:

参数适用范围效果
--stripe-billingorganizationpreset为组织创建 Stripe customer + subscription。与--plan-type free及个人预设互斥
--skip-trial配合--stripe-billing订阅直接active而非trialing,立即以pm_card_visa测试卡扣费
--trial-days N配合--stripe-billing试用期天数,1–30(默认 30),与--skip-trial互斥
# 30 天试用期的 Teams 组织,订阅页面真正可用 dotnet run -- organization -n "Billing Test" -u 3 -d billingtest.example --plan-type teams-monthly --stripe-billing --mangle # 预设组织,无试用期的已付费订阅 dotnet run -- preset --name qa.enterprise-basic --stripe-billing --skip-trial --mangle

成功时输出会多出两行:

StripeCustomer : cus_… StripeSubscription : sub_…

前提条件

  1. bitwarden-seeder-utility的 user secrets 中配置globalSettings:stripe:apiKey,且必须是测试模式密钥(sk_test_…)——生产密钥会被直接拒绝,该工具永不触碰生产计费;
  2. globalSettings:pricingUri必须已设置,以便解析计划定价。appsettings.Development.json 已内置该值(https://billingpricing.qa.bitwarden.pw),因此必须ASPNETCORE_ENVIRONMENT=Development才能生效;
  3. globalSettings:selfHosted必须为false——自建模式下 Pricing Service 永远不会被调用,无法解析任何计划。

三项检查在创建任何实体之前完成:配置错误的 opt-in 会以报错信息和退出码 1 失败,而不是留下半个种子化组织。

环境解析逻辑见 GlobalSettingsFactory:它同时尊重ASPNETCORE_ENVIRONMENTDOTNET_ENVIRONMENT,且回落到 Development 而非 Production,确保本地开发时appsettings.Development.json生效;配置文件从二进制所在目录(AppContext.BaseDirectory)解析,与调用者工作目录无关。

已知边界(Caveats)

  • 种子化的 Teams 与 Enterprise 组织带UseSecretsManager = 1SmSeats为 NULL:只有SmSeats有值时 Secrets Manager 才计入订阅,因此这些组织在数据库里置了标志位,但 Stripe 侧没有对应行项目;
  • 计费发生在数据库提交之后。若此时 Stripe 拒绝请求(密钥过期、网络故障),组织已经提交,命令以非零退出码结束并打印 Stripe 错误及“已写穿的”网关 ID——customer 创建前失败则两者皆 NULL,订阅创建中失败则会留下真实的GatewayCustomerId。重跑前请检查错误信息中的实际状态,并取消孤儿 Stripe customer;
  • 销毁本地数据库不会取消 Stripe 侧订阅;测试模式订阅约 90 天后自动取消。

延伸阅读路径

  • 参数到选项的映射:OrganizationArgs.ToOptions()生成OrganizationVaultOptionsIndividualArgs.ToOptions()生成IndividualUserOptions,均直接驱动 util/Seeder 库的 Recipe;
  • 预设目录与 fixture 清单:util/Seeder/Seeds/docs/presets.md;
  • 任务导向的命令映射:util/Seeder/Seeds/docs/scenarios/README.md;
  • API 侧对应的种子化服务:util/SeederApi 提供 HTTP 形式的同等能力,可与本 CLI 对照理解同一套 Recipe 的两种消费方式。

【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询