From d17e55a1ba0431e5d166d5d40399f5f60b75b8f9 Mon Sep 17 00:00:00 2001 From: Shashank Singh Solanki Date: Sat, 15 May 2021 12:07:24 +0530 Subject: [PATCH 1/2] Fix: Conflicting profile properties between profile and attributes (#543) --- .gitignore | 5 ++++- src/node-saml/saml.ts | 13 +++++++++++-- test/node-saml/tests.spec.ts | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 067641e5..b30518f5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ node_modules/ yarn-error.log .DS_Store .eslintcache -.dir-locals.el \ No newline at end of file +.dir-locals.el + +## Local VS code settings and debug profiles +.vscode \ No newline at end of file diff --git a/src/node-saml/saml.ts b/src/node-saml/saml.ts index aee60fe4..85240de1 100644 --- a/src/node-saml/saml.ts +++ b/src/node-saml/saml.ts @@ -1175,11 +1175,20 @@ class SAML { // if attributes has no AttributeValue child, continue return; } + const name = attribute.$.Name; const value = attribute.AttributeValue; + + // If any property is already present in profile and is also present + // in attributes, then skip the one from attributes. Handle this + // conflict gracefully without returning any error + if (Object.prototype.hasOwnProperty.call(profile, name)) { + return; + } + if (value.length === 1) { - profile[attribute.$.Name] = attrValueMapper(value[0]); + profile[name] = attrValueMapper(value[0]); } else { - profile[attribute.$.Name] = value.map(attrValueMapper); + profile[name] = value.map(attrValueMapper); } }); } diff --git a/test/node-saml/tests.spec.ts b/test/node-saml/tests.spec.ts index 113dca13..d4e318f4 100644 --- a/test/node-saml/tests.spec.ts +++ b/test/node-saml/tests.spec.ts @@ -1904,10 +1904,13 @@ describe("node-saml /", function () { }); }); describe("validatePostRequest()", function () { + const signingKey: any = fs.readFileSync(__dirname + "/../static/key.pem", "ascii"); + const signingCert: any = fs.readFileSync(__dirname + "/../static/cert.pem", "ascii"); let samlObj: SAML; + beforeEach(function () { samlObj = new SAML({ - cert: fs.readFileSync(__dirname + "/../static/cert.pem", "ascii"), + cert: signingCert, }); }); @@ -1981,7 +1984,35 @@ describe("node-saml /", function () { sessionIndex: "1", }); }); + + it("check conflicting profile fields with data from attributes", async () => { + const testSAMLObj = new SAML({ cert: signingCert, issuer: "okta" }); + const xml = + '' + + '' + + "http://idp.example.com/metadata.php" + + "" + + '' + + '' + + "" + + '' + + 'test' + + "" + + "" + + "" + + ""; + const signedXml = signXmlResponse(xml, { privateKey: signingKey }); + const { profile } = await testSAMLObj.validatePostResponseAsync({ + SAMLResponse: Buffer.from(signedXml).toString("base64"), + }); + + should(profile!.issuer).not.be.equal("test"); + }); }); + it("validatePostRequest errors for encrypted nameID with wrong decryptionPvk", async () => { const samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/../static/cert.pem", "ascii"), From 4e19f640ad31f28d4c6a8bcc6f17be9a4064f322 Mon Sep 17 00:00:00 2001 From: Shashank Singh Solanki Date: Thu, 20 May 2021 01:12:40 +0530 Subject: [PATCH 2/2] Add assertion attributes to child object on profile (#543) This attributes are also mounted to profile directly in a non conflicting way. --- src/node-saml/saml.ts | 18 ++++++++++++------ test/node-saml/tests.spec.ts | 6 +----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/node-saml/saml.ts b/src/node-saml/saml.ts index 85240de1..1ed4e127 100644 --- a/src/node-saml/saml.ts +++ b/src/node-saml/saml.ts @@ -1170,13 +1170,21 @@ class SAML { }; if (attributes) { + const profileAttributes: Record = {}; + attributes.forEach((attribute) => { if (!Object.prototype.hasOwnProperty.call(attribute, "AttributeValue")) { // if attributes has no AttributeValue child, continue return; } + const name = attribute.$.Name; - const value = attribute.AttributeValue; + const value = + attribute.AttributeValue.length === 1 + ? attrValueMapper(attribute.AttributeValue[0]) + : attribute.AttributeValue.map(attrValueMapper); + + profileAttributes[name] = value; // If any property is already present in profile and is also present // in attributes, then skip the one from attributes. Handle this @@ -1185,12 +1193,10 @@ class SAML { return; } - if (value.length === 1) { - profile[name] = attrValueMapper(value[0]); - } else { - profile[name] = value.map(attrValueMapper); - } + profile[name] = value; }); + + profile.attributes = profileAttributes; } } diff --git a/test/node-saml/tests.spec.ts b/test/node-saml/tests.spec.ts index d4e318f4..c338709e 100644 --- a/test/node-saml/tests.spec.ts +++ b/test/node-saml/tests.spec.ts @@ -1992,11 +1992,6 @@ describe("node-saml /", function () { '' + "http://idp.example.com/metadata.php" + "" + - '' + - '' + "" + '' + 'test' + @@ -2010,6 +2005,7 @@ describe("node-saml /", function () { }); should(profile!.issuer).not.be.equal("test"); + should(profile!.attributes).containEql({ issuer: "test" }); }); });