From 61c1037d7425b489331f780588cd432f16a27145 Mon Sep 17 00:00:00 2001 From: Bheesham Persaud Date: Thu, 17 Sep 2026 15:59:56 -0400 Subject: [PATCH 1/2] lint: without specifying we'd declare it as a var Jira: IAM-2112 --- tf/tests/samlMappings.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tf/tests/samlMappings.test.js b/tf/tests/samlMappings.test.js index 32e107e..a0c3346 100644 --- a/tf/tests/samlMappings.test.js +++ b/tf/tests/samlMappings.test.js @@ -374,7 +374,7 @@ describe("Acoustic SAML tests", () => { async (clientID) => { _event.client.client_id = clientID; - expectedSamlAttributes = { + const expectedSamlAttributes = { Nameid: _event.user.email, email: _event.user.email, firstName: _event.user.given_name, From e435339311a6696955c02b6d4b6c0908c0790611 Mon Sep 17 00:00:00 2001 From: Bheesham Persaud Date: Thu, 17 Sep 2026 16:01:26 -0400 Subject: [PATCH 2/2] stripe: Users may have multiple roles assigned We didn't hit this before because owners of LDAP groups may not have been _in_ the groups. With PeopleMo, curators are in the groups. Jira: IAM-2112 --- tf/actions/samlMappings.js | 17 +++++++++++++---- tf/tests/samlMappings.test.js | 18 ++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/tf/actions/samlMappings.js b/tf/actions/samlMappings.js index 1966572..6d09770 100644 --- a/tf/actions/samlMappings.js +++ b/tf/actions/samlMappings.js @@ -99,6 +99,9 @@ exports.onExecutePostLogin = async (event, api) => { roles: [{ role: "view_only", account: "acct_1EJOaaJNcmPzuWtR" }], }, ]; + // Each user may have multiple roles assigned to them per account. + // We gather each of their assigned roles, and then assert it afterwards. + const accountRoles = {}; for (const rule of groupToRoles) { const usersAllowed = rule.users ?? []; const isAllowedByEmail = usersAllowed.includes(event.user.email); @@ -106,13 +109,19 @@ exports.onExecutePostLogin = async (event, api) => { if (!(isAllowedByEmail || isAllowedByGroup)) { continue; } + // Gather the user's roles in each account. for (const role of rule.roles) { - api.samlResponse.setAttribute( - `Stripe-Role-${role.account}`, - role.role - ); + const account = `Stripe-Role-${role.account}`; + if (account in accountRoles) { + accountRoles[account].push(role.role); + } else { + accountRoles[account] = [role.role]; + } } } + for (const account in accountRoles) { + api.samlResponse.setAttribute(account, accountRoles[account]); + } break; } // acoustic-stage, acoustic-prod diff --git a/tf/tests/samlMappings.test.js b/tf/tests/samlMappings.test.js index a0c3346..8991194 100644 --- a/tf/tests/samlMappings.test.js +++ b/tf/tests/samlMappings.test.js @@ -332,7 +332,7 @@ describe("Stripe-Subplat SAML tests", () => { _event.user.app_metadata.groups = _event.user.app_metadata.groups || []; _event.user.app_metadata.groups.push(group); expectedSamlAttributes = { - "Stripe-Role-acct_1EJOaaJNcmPzuWtR": role, + "Stripe-Role-acct_1EJOaaJNcmPzuWtR": [role], }; } else { delete _event.user.groups; @@ -355,7 +355,21 @@ describe("Stripe-Subplat SAML tests", () => { _event.client.client_id = "cEfnJekrSStxxxBascTjNEDAZVUPAIU2"; _event.user.email = "araccounting@mozilla.com"; const expectedSamlAttributes = { - "Stripe-Role-acct_1EJOaaJNcmPzuWtR": "admin", + "Stripe-Role-acct_1EJOaaJNcmPzuWtR": ["admin"], + }; + await onExecutePostLogin(_event, api); + expect(api.samlResponse.setAttribute).toHaveBeenCalled(); + expect(_samlAttributes).toEqual(expectedSamlAttributes); + }); + + test("User with multiple roles", async () => { + _event.client.client_id = "cEfnJekrSStxxxBascTjNEDAZVUPAIU2"; + _event.user.app_metadata.groups = [ + "mozilliansorg_stripe_subplat_analyst", + "mozilliansorg_stripe_subplat_viewonly", + ]; + const expectedSamlAttributes = { + "Stripe-Role-acct_1EJOaaJNcmPzuWtR": ["analyst", "view_only"], }; await onExecutePostLogin(_event, api); expect(api.samlResponse.setAttribute).toHaveBeenCalled();