From 6b8cd7e564160e1fb7493b2d8afc9243a2715237 Mon Sep 17 00:00:00 2001 From: Bilal Godil Date: Thu, 30 Apr 2026 14:45:48 -0700 Subject: [PATCH] chore(backend): defer SAML seed + node-saml dep to stacked backend PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes seedSamlConnections (and its STACK_SEED_ENABLE_SAML callsite) plus the @node-saml/node-saml dependency from this PR. Both depend on config.auth.saml schema entries that don't exist on this branch yet — the seed wrote overrides that were silently dropped during config normalization, and node-saml had no consumer here. They land together in the stacked backend PR alongside the schema and the SAML protocol wrapper that actually imports node-saml. --- apps/backend/package.json | 1 - apps/backend/src/lib/seed-dummy-data.ts | 73 ------------------------- pnpm-lock.yaml | 70 ++---------------------- 3 files changed, 6 insertions(+), 138 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index 5264adf3d..b0500582d 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -61,7 +61,6 @@ "@aws-sdk/client-s3": "^3.855.0", "@clickhouse/client": "^1.14.0", "@node-oauth/oauth2-server": "^5.1.0", - "@node-saml/node-saml": "^5.0.0", "@openrouter/ai-sdk-provider": "2.2.3", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.53.0", diff --git a/apps/backend/src/lib/seed-dummy-data.ts b/apps/backend/src/lib/seed-dummy-data.ts index 479cd1088..a9ad48427 100644 --- a/apps/backend/src/lib/seed-dummy-data.ts +++ b/apps/backend/src/lib/seed-dummy-data.ts @@ -1939,69 +1939,6 @@ async function seedBulkSignupsAndActivity(options: { console.log(`[seed-activity] Events: $token-refresh=${tokenRefreshCount} $page-view=${pageViewCount} $click=${clickCount} total=${clickhouseRows.length}`); } -/** - * Pre-creates two SAML connections (acme + globex) on the dummy project that - * point at the local mock SAML IdP. Gated on STACK_SEED_ENABLE_SAML='true'. - * Fetches the mock IdP's metadata at seed time so the seeded cert matches - * the cert the mock generated at startup — the mock currently regenerates - * keys per restart, so re-seed if you restart the mock. - */ -async function seedSamlConnections(projectId: string): Promise { - const mockUrl = getEnvVariable("STACK_MOCK_SAML_URL", "http://localhost:8115"); - const tenants: Array<{ slug: string, displayName: string, domain: string }> = [ - { slug: "acme", displayName: "Acme Corp SSO", domain: "acme.test" }, - { slug: "globex", displayName: "Globex SAML", domain: "globex.test" }, - ]; - - const fetched = await Promise.all( - tenants.map(async (t) => { - const res = await fetch(`${mockUrl}/idp/${t.slug}/metadata`); - if (!res.ok) { - throw new Error(`Mock SAML IdP at ${mockUrl}/idp/${t.slug}/metadata returned ${res.status} — is the mock running?`); - } - const xml = await res.text(); - // Inline minimal metadata parse to avoid a circular import. Format is - // exactly what the mock emits, so a regex is enough; the production - // parser at apps/backend/src/saml/metadata-parser.tsx is the - // robust one used by the dashboard "paste metadata" form. - const entityIdMatch = xml.match(/entityID="([^"]+)"/); - const ssoUrlMatch = xml.match(/Binding="urn:oasis:names:tc:SAML:2\.0:bindings:HTTP-Redirect"[^>]*Location="([^"]+)"/); - const certMatch = xml.match(/([\s\S]+?)<\/X509Certificate>/); - if (!entityIdMatch || !ssoUrlMatch || !certMatch) { - throw new Error(`Could not parse mock IdP metadata for tenant ${t.slug}`); - } - return { - ...t, - idpEntityId: entityIdMatch[1], - idpSsoUrl: ssoUrlMatch[1], - idpCertificate: certMatch[1].replace(/\s+/g, ""), - }; - }), - ); - - // Set the entire connection entry as a single value, not as deep - // dot-keys — config normalization with onDotIntoNonObject="ignore" - // drops dot-keys that try to navigate into a record entry that - // doesn't yet exist (same convention as auth.oauth.providers). - const overlay: Parameters[0]["environmentConfigOverrideOverride"] = {}; - for (const f of fetched) { - overlay[`auth.saml.connections.${f.slug}`] = { - displayName: f.displayName, - allowSignIn: true, - domain: f.domain, - idpEntityId: f.idpEntityId, - idpSsoUrl: f.idpSsoUrl, - idpCertificate: f.idpCertificate, - }; - } - - await overrideEnvironmentConfigOverride({ - projectId, - branchId: DEFAULT_BRANCH_ID, - environmentConfigOverrideOverride: overlay, - }); -} - /** * Creates a new project and fills it with dummy data (users, teams, payments, emails, analytics events). * Used by both the seed script and the preview project creation endpoint. @@ -2153,16 +2090,6 @@ export async function seedDummyProject(options: SeedDummyProjectOptions): Promis }), ]); - // Run sequentially after the parallel block. Both this and the - // `payments.testMode` write above target the same environment config, - // and `overrideEnvironmentConfigOverride` is read-modify-write — running - // them in parallel races and one write clobbers the other (TODO at - // config.ts:491 already documents this). Sequencing avoids the race - // until the underlying override is wrapped in a serializable txn. - if (getEnvVariable("STACK_SEED_ENABLE_SAML", "false") === "true") { - await seedSamlConnections(projectId); - } - await seedDummyTransactions({ prisma: dummyPrisma, tenancyId: dummyTenancy.id, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc67ad480..3cef648e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -126,9 +126,6 @@ importers: '@node-oauth/oauth2-server': specifier: ^5.1.0 version: 5.1.0 - '@node-saml/node-saml': - specifier: ^5.0.0 - version: 5.1.0 '@openrouter/ai-sdk-provider': specifier: 2.2.3 version: 2.2.3(ai@6.0.81(zod@3.25.76))(zod@3.25.76) @@ -870,12 +867,6 @@ importers: apps/mock-saml-idp: dependencies: - '@types/express': - specifier: ^5.0.0 - version: 5.0.0 - '@types/node-forge': - specifier: ^1.3.11 - version: 1.3.14 express: specifier: ^4.21.2 version: 4.21.2 @@ -889,6 +880,12 @@ importers: specifier: ^2.10.0 version: 2.12.0 devDependencies: + '@types/express': + specifier: ^5.0.0 + version: 5.0.0 + '@types/node-forge': + specifier: ^1.3.11 + version: 1.3.14 tsx: specifier: ^4.16.2 version: 4.21.0 @@ -5989,10 +5986,6 @@ packages: resolution: {integrity: sha512-sYvqL1GeZLRSwgl++/oOzxJj/ZBe2yXnp6E5LGNQ5qjpn0+t/dwquXILUe3Sk2Y8/wU7XeRxToOtBVeSVkuJag==} engines: {node: '>=16.0.0'} - '@node-saml/node-saml@5.1.0': - resolution: {integrity: sha512-t3cJnZ4aC7HhPZ6MGylGZULvUtBOZ6FzuUndaHGXjmIZHXnLfC/7L8a57O9Q9V7AxJGKAiRM5zu2wNm9EsvQpw==} - engines: {node: '>= 18'} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -10579,9 +10572,6 @@ packages: '@types/qrcode@1.5.5': resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==} - '@types/qs@6.15.0': - resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} - '@types/qs@6.9.15': resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} @@ -10654,12 +10644,6 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/xml-encryption@1.2.4': - resolution: {integrity: sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==} - - '@types/xml2js@0.4.14': - resolution: {integrity: sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==} - '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -19408,9 +19392,6 @@ packages: resolution: {integrity: sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w==} engines: {node: '>=16'} - xml-encryption@3.1.0: - resolution: {integrity: sha512-PV7qnYpoAMXbf1kvQkqMScLeQpjCMixddAKq9PtqVrho8HnYbBOWNfG0kA4R7zxQDo7w9kiYAyzS/ullAyO55Q==} - xml-escape@1.1.0: resolution: {integrity: sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==} @@ -19437,10 +19418,6 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} - xmlbuilder@15.1.1: - resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} - engines: {node: '>=8.0'} - xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} @@ -23801,23 +23778,6 @@ snapshots: basic-auth: 2.0.1 type-is: 1.6.18 - '@node-saml/node-saml@5.1.0': - dependencies: - '@types/debug': 4.1.12 - '@types/qs': 6.15.0 - '@types/xml-encryption': 1.2.4 - '@types/xml2js': 0.4.14 - '@xmldom/is-dom-node': 1.0.1 - '@xmldom/xmldom': 0.8.13 - debug: 4.4.3 - xml-crypto: 6.1.2 - xml-encryption: 3.1.0 - xml2js: 0.6.2 - xmlbuilder: 15.1.1 - xpath: 0.0.34 - transitivePeerDependencies: - - supports-color - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -30420,8 +30380,6 @@ snapshots: dependencies: '@types/node': 22.19.0 - '@types/qs@6.15.0': {} - '@types/qs@6.9.15': {} '@types/range-parser@1.2.7': {} @@ -30508,14 +30466,6 @@ snapshots: dependencies: '@types/node': 22.19.0 - '@types/xml-encryption@1.2.4': - dependencies: - '@types/node': 22.19.0 - - '@types/xml2js@0.4.14': - dependencies: - '@types/node': 22.19.0 - '@types/yauzl@2.10.3': dependencies: '@types/node': 22.19.0 @@ -42025,12 +41975,6 @@ snapshots: '@xmldom/xmldom': 0.8.13 xpath: 0.0.33 - xml-encryption@3.1.0: - dependencies: - '@xmldom/xmldom': 0.8.13 - escape-html: 1.0.3 - xpath: 0.0.32 - xml-escape@1.1.0: {} xml-js@1.6.11: @@ -42055,8 +41999,6 @@ snapshots: xmlbuilder@11.0.1: {} - xmlbuilder@15.1.1: {} - xmlchars@2.2.0: {} xpath@0.0.32: {}