PM-40665 - Seed SSO configuration and a realistic local-sso dev org in the Seeder#8014
PM-40665 - Seed SSO configuration and a realistic local-sso dev org in the Seeder#8014theMickster wants to merge 13 commits into
Conversation
Add the SsoConfigSeeder factory that builds a SAML SsoConfig, the SeedPreset sso block plus an optional fixed organization id, and the LocalSamlIdp constants for the bundled dev IdP. No pipeline wiring yet.
The sso preset block and the Enterprise UseSso flag already existed, but no step ever wrote an SsoConfig row, so seeded SSO orgs were only half-configured and could not broker a login. This wires the SSO factory into the pipeline to close that gap, following the existing OrganizationDomain seed pattern. - CreateSsoConfigStep reads the org from context, sets its Identifier, fetches the IdP signing certificate live from the local IdP metadata endpoint (XXE-safe reader), and stages the SsoConfig. - WithSso(...) on the recipe builder (guarded on an existing org) and PresetLoader consumption of the sso block; an optional fixed org id is threaded through CreateOrganizationStep/OrganizationSeeder so golden orgs reproduce the same GUID. - BulkCommitter commits the single row via EF Add (long identity PK); the SSO identifier is surfaced on the pipeline and organization result models. The certificate is fetched at seed time rather than baked into the repo so a ~1 KB base64 blob never trips secret/SAST scanners, and it auto-tracks the dev IdP image's cert.
After seeding an SSO org the operator still has to point the local IdP at it, and the SP entity id / ACS URL are easy to get subtly wrong by hand — they embed the org GUID and depend on the Sso launch-profile port. Rather than leave that as a manual copy-from-the-Admin-Console step, the CLI now prints the exact wiring. - ConsoleOutput.PrintSsoWiring emits a ready-to-paste dev/.env block (IDP_SP_ENTITY_ID, IDP_SP_ACS_URL) plus the login identifier for the seeded org. - PresetCommand invokes it only when the seed result carries an SSO identifier (set on the SAML path together with the SsoConfig), so non-SSO and OIDC presets print nothing extra.
tde-vault (7 login stubs) and sso-vault (a single login) were thin, low-value cipher fixtures that gave their presets almost no content. Consolidating on the shared enterprise-basic fixture — enriched in the following commit — lets those SSO/TDE presets seed a realistic vault and drops two near-empty fixtures. - Delete ciphers/tde-vault.json and ciphers/sso-vault.json. - Re-point sso-enterprise and tde-enterprise to the enterprise-basic cipher fixture. (The golden local-sso preset is added later already pointing at enterprise-basic.)
The roster's five members were role-name placeholders (Enterprise Owner, Admin, User, Custom, Custom-Two) under a single All Members group — it didn't resemble a real company. It's also the roster the golden local-sso org now uses, so these members/groups/collections are exactly what a developer sees after an SSO login. - Replace the placeholders with 25 realistically-named members across owner/admin/user/custom roles. - Add Finance, Human Resources, and Information Technology groups with sensible department membership. All Members is retained and repopulated with everyone, since the existing collections grant against it. - Add four named collections: Finance, Human Resources, and Information Technology (each managed by its department group and by the owner directly) plus a Company-Wide collection for All Members. The legacy placeholder collections are retained (the Default collection's owner grant is updated to the renamed owner); the new collections are added alongside, preserving existing access.
The fixture was 24 near-empty items (login stubs with test/123, several empty login objects, one card, one secure note) covering only three cipher types — not enough to demo the client or exercise the attachment and lifecycle paths. - Rewrite it as 45 realistically-named items covering all eight cipher types (login, card, identity, secureNote, sshKey, bankAccount, driversLicense, passport) with richer fields: notes, uris, custom fields, totp, password history. - Attach bundled sample bodies to 18 items (~40%) across the v0/v1/v2 schemes; every v2 attachment sits on a cipherKey host per the scheme invariant. - Seed 4 archived and 4 soft-deleted items so those states are present. - Extend FixtureParsingTests to assert enterprise-basic covers all eight types, carries archived/deleted items, and references only embedded attachment bodies — bundled here because those assertions only pass against the enriched fixture. All data is fake/mock; no new attachment files are added — existing bundled bodies are reused.
Standing up a SAML SSO org by hand — Enterprise org, Admin Console config, certificate extraction, .env wiring, IdP account mapping — is fiddly and easy to get subtly wrong. This adds a fixed, reproducible golden org so 'preset --name features.local-sso' (seeded without --mangle) yields a login-ready, populated SSO org against the bundled SimpleSAMLphp IdP. - Add the local-sso preset: fixed org GUID, SAML masterPassword, on the enriched enterprise-basic roster and ciphers, with collectionAssignments routing all 45 ciphers into the Finance/HR/IT/Company-Wide collections so the owner logs in to a populated vault. (Explicit routing leaves the legacy placeholder collections empty in this org.) - Pre-wire the committed dev templates: the .env template sets IDP_SP_ENTITY_ID/ACS for the fixed org GUID, and the IdP authsources template maps the 'owner' login to the seeded owner (dana.whitfield@verdant.example) — so a fresh checkout logs in out of the box. The live dev/.env and dev/authsources.php are gitignored; only the .example templates are committed.
Keep the preset catalog in step with the fixture changes so the docs don't drift. - Update the sso-enterprise and tde-enterprise rows to show the enterprise-basic cipher fixture (was sso-vault/tde-vault). - Add a local-sso row and a note: golden fixed-GUID SAML 2.0 org, seed without --mangle, log in as owner/password, and the Azurite requirement now that enterprise-basic carries attachments. The formatter re-aligned the QA table columns as well (whitespace only; no QA content changed).
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE Reviewed a dev-tooling PR that makes SAML SSO seedable in Code Review Details
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8014 +/- ##
==========================================
+ Coverage 62.36% 62.44% +0.08%
==========================================
Files 2286 2290 +4
Lines 99673 99752 +79
Branches 9003 9004 +1
==========================================
+ Hits 62157 62289 +132
+ Misses 35344 35291 -53
Partials 2172 2172 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… expression' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
🎟️ Tracking
PM-40665 - Seed a working SSO configuration through the preset catalog
📔 Objective
Dev-tooling enhancement to the Seeder (
util/Seeder)enterprise-basicfixtures into a realistic org and add a goldenlocal-ssopreset that seeds a reproducible, populated SSO org against the bundled local IdP.Key changes
SsoConfigSeederandCreateSsoConfigStep— build and seed a SAMLSsoConfig, fetching the IdP signing certificate live from the local IdP metadata endpoint at seed time (XXE-safe), so no certificate is baked into the repo.ssopreset block and aWithSso(...)recipe step consumed byPresetLoader; commit the config via EFAddinBulkCommitter(single row,longidentity PK).idthroughCreateOrganizationStep/OrganizationSeederso a golden org reproduces the same GUID across clean-slate seeds..envSSO wiring block from the CLI after seeding a preset that produced anSsoConfig.tde-vault/sso-vaultcipher fixtures; re-pointsso-enterprise/tde-enterprisetoenterprise-basic.enterprise-basicroster to 25 realistic members with groups and matching collections.enterprise-basiccipher fixture to 45 items across all eight cipher types, ~40% carrying attachments (bundled sample bodies acrossv0/v1/v2), plus archived and soft-deleted items.local-ssopreset (fixed org GUID, SAMLmasterPassword) withcollectionAssignmentsrouting every cipher into the department collections; commit local-IdP wiring templates so it logs in against the bundled SimpleSAMLphp IdP out of the box.Not changed: the SSO broker app, Identity, and the Rust SDK — the Seeder writes the same server-side
SsoConfig/OrganizationUserrecords a real client produces; it never fabricates key material or device-trust state.🧪 Testing
Expand for seed + login verification
Step 1: Fixture parse/coverage tests
dotnet test test/SeederApi.IntegrationTest/ --filter FixtureParsingTests→ 6/6 pass (fixture deserializes, all eight cipher types present, attachment bodies embedded), alongside the SSO factory/step tests.Step 2: Clean-slate seed
preset --name features.local-ssoagainst a fresh DB with Azurite running seeds orga1b2c3d4-…:configType=2,memberDecryptionType=0,spUniqueEntityId=trueStep 3: End-to-end SSO login
Logged in through the local SimpleSAMLphp IdP as the seeded owner and landed in a populated Password Manager vault. The owner reaches 39/40 collections via
UserCollectionDetails(the 40th is the intentional No-Access placeholder).