diff --git a/.travis.yml b/.travis.yml index 01eb8203..efe5851a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,5 @@ language: node_js node_js: - - "0.10" - - "0.12" - - "iojs" - "4.0" - "stable" diff --git a/README.md b/README.md index f6a16b0e..3e0b25ab 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,10 @@ The `decryptionCert` argument should be a public certificate matching the `decry Passport-SAML uses the HTTP Redirect Binding for its `AuthnRequest`s (unless overridden with the `authnRequestBinding` parameter), and expects to receive the messages back via the HTTP POST binding. -Authentication requests sent by Passport-SAML can be signed using RSA-SHA1. To sign them you need to provide a private key in the PEM format via the `privateCert` configuration key. For example: +Authentication requests sent by Passport-SAML can be signed using RSA-SHA1. To sign them you need to provide a private key in the PEM format via the `privateCert` configuration key. The certificate +should start with `-----BEGIN PRIVATE KEY-----` on its own line and end with `-----END PRIVATE KEY-----` on its own line. + +For example: ```javascript privateCert: fs.readFileSync('./cert.pem', 'utf-8') diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index b0193688..36ece2a0 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -94,12 +94,7 @@ SAML.prototype.getCallbackUrl = function (req) { }; SAML.prototype.generateUniqueID = function () { - var chars = "abcdef0123456789"; - var uniqueID = ""; - for (var i = 0; i < 20; i++) { - uniqueID += chars.substr(Math.floor((Math.random()*15)), 1); - } - return uniqueID; + return crypto.randomBytes(10).toString('hex'); }; SAML.prototype.generateInstant = function () { @@ -517,15 +512,23 @@ SAML.prototype.validateSignature = function (fullXml, currentNode, cert) { SAML.prototype.validatePostResponse = function (container, callback) { var self = this; - var xml = new Buffer(container.SAMLResponse, 'base64').toString('utf8'); - var doc = new xmldom.DOMParser().parseFromString(xml); - var inResponseTo = xpath(doc, "/*[local-name()='Response']/@InResponseTo"); - if(inResponseTo){ - inResponseTo = inResponseTo.length ? inResponseTo[0].nodeValue : null; - } + var xml, doc, inResponseTo; Q.fcall(function(){ + xml = new Buffer(container.SAMLResponse, 'base64').toString('utf8'); + doc = new xmldom.DOMParser({ + }).parseFromString(xml); + + if (!doc.hasOwnProperty('documentElement')) + throw new Error('SAMLResponse is not valid base64-encoded XML'); + + inResponseTo = xpath(doc, "/*[local-name()='Response']/@InResponseTo"); + + if(inResponseTo){ + inResponseTo = inResponseTo.length ? inResponseTo[0].nodeValue : null; + } + if(self.options.validateInResponseTo){ if (inResponseTo) { return Q.ninvoke(self.cacheProvider, 'get', inResponseTo) diff --git a/package.json b/package.json index b4ec0882..3c73d2a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "passport-saml", - "version": "0.20.1", + "version": "0.20.2", "license" : "MIT", "keywords": [ "saml", @@ -45,7 +45,7 @@ "sinon": "^2.1.0" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 4" }, "scripts": { "test": "mocha", diff --git a/test/tests.js b/test/tests.js index 149bd2ed..23639751 100644 --- a/test/tests.js +++ b/test/tests.js @@ -506,6 +506,14 @@ describe( 'passport-saml /', function() { }); describe( 'saml.js / ', function() { + it( 'generateUniqueID should generate 20 char IDs', function( done ) { + var samlObj = new SAML( { entryPoint: "foo" } ); + for(var i = 0; i < 200; i++){ + samlObj.generateUniqueID().length.should.eql(20); + } + done(); + }); + it( 'generateLogoutRequest', function( done ) { var expectedRequest = { 'samlp:LogoutRequest': @@ -732,6 +740,15 @@ describe( 'passport-saml /', function() { }); describe("validatePostResponse checks /", function() { + it('response with junk content should explain the XML or base64 is not valid', function(done) { + var samlObj = new SAML( { cert: TEST_CERT }); + samlObj.validatePostResponse({SAMLResponse: "BOOM"} , function( err, profile, logout ) { + should.exist( err ); + err.message.should.match( /SAMLResponse is not valid base64-encoded XML/ ); +// should.exist( err.statusXml ); + done(); + }); + }); it('response with error status message should generate appropriate error', function(done) { var xml = 'https://idp.testshib.org/idp/shibbolethRequired NameID format not supported'; var base64xml = new Buffer( xml ).toString('base64');