Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add assertion attributes to child object on profile (passport-saml#543) #5

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/workflow.yml
Expand Up @@ -24,7 +24,11 @@ jobs:
- run: npm ci
- run: npm test
- run: npm ci
- run: npm --depth 9999 update
- run: npm update
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this then working in passport-saml if it isn't working here in node-saml?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried running these commands in the same sequence with passport-saml's package.json on this repo, but still the command was failing for this repo and not for passport-saml.

# Remove the node_modules and re-install with updated package-lock.json
# because npm update command breaks some of the npm-cli packages
- run: rm -rf node_modules
- run: npm ci
- run: npm test
env:
CI: true
25 changes: 20 additions & 5 deletions src/saml.ts
Expand Up @@ -1167,18 +1167,33 @@ class SAML {
};

if (attributes) {
const profileAttributes: Record<string, unknown> = {};

attributes.forEach((attribute) => {
if (!Object.prototype.hasOwnProperty.call(attribute, "AttributeValue")) {
// if attributes has no AttributeValue child, continue
return;
}
const value = attribute.AttributeValue;
if (value.length === 1) {
profile[attribute.$.Name] = attrValueMapper(value[0]);
} else {
profile[attribute.$.Name] = value.map(attrValueMapper);

const name = attribute.$.Name;
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
// conflict gracefully without returning any error
if (Object.prototype.hasOwnProperty.call(profile, name)) {
return;
}

profile[name] = value;
});

profile.attributes = profileAttributes;
}
}

Expand Down
33 changes: 32 additions & 1 deletion test/tests.spec.ts
Expand Up @@ -1895,10 +1895,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,
});
});

Expand Down Expand Up @@ -1972,7 +1975,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 =
'<Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" ID="response0">' +
'<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0">' +
"<saml:Issuer>http://idp.example.com/metadata.php</saml:Issuer>" +
"<saml2:AttributeStatement>" +
'<saml2:Attribute Name="attributeName" ' +
'NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">' +
'<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" ' +
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
'xsi:type="xs:string"/>' +
"</saml2:Attribute>" +
'<saml2:Attribute Name="issuer" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">' +
'<saml2:AttributeValue xsi:type="xs:string">test</saml2:AttributeValue>' +
"</saml2:Attribute>" +
"</saml2:AttributeStatement>" +
"</saml2:Assertion>" +
"</Response>";
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"),
Expand Down