Skip to content

Add self-hosted premium license signing to the seeder - #8178

Open
nthompson-bitwarden wants to merge 4 commits into
mainfrom
seeder/premium-selfhost-license-signing
Open

Add self-hosted premium license signing to the seeder#8178
nthompson-bitwarden wants to merge 4 commits into
mainfrom
seeder/premium-selfhost-license-signing

Conversation

@nthompson-bitwarden

@nthompson-bitwarden nthompson-bitwarden commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🎟️ Tracking

N/A

📔 Objective

The seeder could stand up self-hosted premium users, but it never produced the signed license they need because LicensingService exposes no seam to inject a private cert while staying self-hosted. So those accounts weren't actually usable as premium on a self-hosted instance, which validates premium status by reading a license file on every login. This PR teaches the seeder to sign and write real self-hosted premium user licenses, and to clean them up on teardown.

  • Adds SeederLicenseSigner, which signs a user license JWT with the configured licensing certificate (licenseCertificatePath/licenseCertificatePassword), mirroring LicensingService.GenerateToken so issuer, algorithm, and lifetime stay in sync with production.
  • Wires signing through SelfHostLicenseService, which now returns a structured LicenseWriteOutcome instead of throwing — both call sites (GenerateSelfHostUserLicenseStep in the pipeline and SingleUserScene) consume that outcome, and the scene surfaces PremiumLicenseWritten/PremiumLicenseWarning in its result.
  • Keeps it best-effort: with no usable signing certificate, or on a file/crypto error, the seeder logs a warning and skips license generation instead of failing the seed. This replaces the previous scattered try/catch + Console.WriteLine blocks with centralized, logged handling.
  • Extends DestroySceneCommand to delete the license files the seeder wrote ({LicenseDirectory}/user/{userId}.json) when tearing down a self-hosted scene, so cleanup doesn't leave orphaned licenses behind. File errors there are logged and swallowed so teardown never aborts.

📸 Screenshots

Sign self-hosted premium user licenses during seeding via a new
SeederLicenseSigner (certificate loaded lazily and cached across runs),
wire it into SeederDependencies and the pipeline, write signed licenses
through SelfHostLicenseService, and delete license files on the destroy
scene command. Includes unit and integration coverage.
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the new SeederLicenseSigner / LicenseWriteOutcome plumbing, the SingleUserScene and pipeline call sites, the DestroySceneCommand license-file cleanup, and the DI wiring across SeederApi and SeederUtility. JWT construction matches LicensingService.GenerateToken (issuer, RsaSha256Signature, one-year lifetime, jti claim), and ILicenseClaimsFactory<User> / IGlobalSettings are registered in both hosts that resolve the new singleton. Replacing the scattered Console.WriteLine catch blocks with a single logged, structured outcome is a clear improvement, and the tests cover the skip, failure, and propagation paths. Two low-severity findings, neither blocking.

Code Review Details
  • 🎨 : HasPrivateKey doesn't guarantee an RSA key; a non-RSA cert throws ArgumentNullException past the best-effort handling
    • util/Seeder/Services/SeederLicenseSigner.cs:95
  • ❓ : License cleanup gates on globalSettings.SelfHosted while the write gates on the per-request/--self-hosted flag, which can orphan license files
    • util/SeederApi/Commands/DestroySceneCommand.cs:105

Comment thread util/SeederApi/Commands/DestroySceneCommand.cs Outdated
Comment thread util/Seeder/Services/SeederLicenseSigner.cs Outdated
@nthompson-bitwarden nthompson-bitwarden added the t:misc Change Type - ¯\_(ツ)_/¯ Prefer using other type labels label Aug 10, 2026
Comment thread util/Seeder/Services/SeederLicenseSigner.cs Dismissed
Comment thread util/SeederApi/Commands/DestroySceneCommand.cs Dismissed
Comment thread test/SeederApi.IntegrationTest/Commands/DestroySceneCommandTests.cs Dismissed
Comment thread test/SeederApi.IntegrationTest/Commands/DestroySceneCommandTests.cs Dismissed
Comment thread test/SeederApi.IntegrationTest/Commands/DestroySceneCommandTests.cs Dismissed
Comment thread test/SeederApi.IntegrationTest/Commands/DestroySceneCommandTests.cs Dismissed
Comment thread util/SeederApi/Commands/DestroySceneCommand.cs Dismissed
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.40%. Comparing base (7afee45) to head (ed199d8).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8178      +/-   ##
==========================================
+ Coverage   63.37%   63.40%   +0.02%     
==========================================
  Files        2337     2337              
  Lines      101456   101456              
  Branches     9167     9167              
==========================================
+ Hits        64302    64324      +22     
+ Misses      34943    34913      -30     
- Partials     2211     2219       +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

t:misc Change Type - ¯\_(ツ)_/¯ Prefer using other type labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant