// SAML service-provider (relying-party) registry. Backed by _idp_saml_sps. The // SSO endpoint only issues an assertion to a registered SP and only to one of // its allow-listed ACS URLs -- this is what prevents a forged AuthnRequest from // having a signed assertion delivered to an attacker-chosen Destination. The // signing_cert is a PUBLIC X.509 cert (no sealing, unlike client secrets); when // present and want_authn_requests_signed is set, the SSO endpoint verifies the // AuthnRequest signature against it. const db = require("@saltcorn/data/db"); const { TABLE_SAML_SPS } = require("../constants"); // True if the requested ACS exactly matches one of the SP's allow-listed URLs. // Exact-string match is the secure default (no trailing-slash/scheme fuzzing). const acsAllowed = (row, acs) => { if (!row || !acs) { return false; } let list = []; try { list = JSON.parse(row.acs_urls); } catch (e) { return false; } return Array.isArray(list) && list.includes(acs); }; const acsUrls = (row) => { try { const list = JSON.parse(row.acs_urls); return Array.isArray(list) ? list : []; } catch (e) { return []; } }; const createSp = async (opts) => { await db.insert(TABLE_SAML_SPS, { entity_id: opts.entityId, label: opts.label || null, acs_urls: JSON.stringify(opts.acsUrls || []), signing_cert: opts.signingCert || null, want_authn_requests_signed: opts.wantSigned ? 1 : 0, created_at: new Date().toISOString() }, { noid: true }); return { entity_id: opts.entityId }; }; const deleteSp = async (entityId) => { await db.deleteWhere(TABLE_SAML_SPS, { entity_id: entityId }); }; const getSp = async (entityId) => { return await db.selectMaybeOne(TABLE_SAML_SPS, { entity_id: entityId }); }; const listSps = async () => { return await db.select(TABLE_SAML_SPS, {}, { orderBy: "entity_id" }); }; // Coerce the stored INTEGER 0/1 (sqlite) or boolean (pg driver) to a real bool. const wantsSignedRequests = (row) => { return !!(row && row.want_authn_requests_signed); }; module.exports = { acsAllowed, acsUrls, createSp, deleteSp, getSp, listSps, wantsSignedRequests };