From a0fbc75bf3fc2ca76aaf153d40d31868e2a7be91 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Fri, 26 Apr 2019 16:09:17 -0400 Subject: [PATCH 1/2] Fix broken tests --- lib/passport-saml/saml.js | 4 +- multiSamlStrategy.js | 2 +- test/multiSamlStrategy.js | 112 ++++-- test/samlTests.js | 162 +++++--- test/tests.js | 761 ++++++++++++++++++++++++-------------- 5 files changed, 679 insertions(+), 362 deletions(-) diff --git a/lib/passport-saml/saml.js b/lib/passport-saml/saml.js index 31b10703..dac0020d 100644 --- a/lib/passport-saml/saml.js +++ b/lib/passport-saml/saml.js @@ -756,7 +756,7 @@ SAML.prototype.validateRedirect = function(container, callback) { var dom = new xmldom.DOMParser().parseFromString(inflated.toString()); var parserConfig = { explicitRoot: true, - explicitCharKey: true, + explicitCharkey: true, tagNameProcessors: [xml2js.processors.stripPrefix] }; var parser = new xml2js.Parser(parserConfig); @@ -1154,7 +1154,7 @@ function processValidlySignedPostRequest(self, doc, callback) { } var sessionIndex = request.SessionIndex; if (sessionIndex) { - profile.sessionIndex = sessionIndex[0]; + profile.sessionIndex = sessionIndex[0]._; } callback(null, profile, true); diff --git a/multiSamlStrategy.js b/multiSamlStrategy.js index 123d84e4..46f8bdb1 100644 --- a/multiSamlStrategy.js +++ b/multiSamlStrategy.js @@ -52,7 +52,7 @@ MultiSamlStrategy.prototype.logout = function (req, options) { MultiSamlStrategy.prototype.generateServiceProviderMetadata = function( req, decryptionCert, signingCert, next ) { var self = this; - return this._getSamlOptions(req, function (err, samlOptions) { + return this._options.getSamlOptions(req, function (err, samlOptions) { if (err) { return next(err); } diff --git a/test/multiSamlStrategy.js b/test/multiSamlStrategy.js index 1acc0912..72939ded 100644 --- a/test/multiSamlStrategy.js +++ b/test/multiSamlStrategy.js @@ -32,9 +32,13 @@ describe('strategy#authenticate', function() { it('calls super with request and auth options', function(done) { var superAuthenticateStub = this.superAuthenticateStub; function getSamlOptions (req, fn) { - fn(); - sinon.assert.calledOnce(superAuthenticateStub); - done(); + try { + fn(); + sinon.assert.calledOnce(superAuthenticateStub); + done(); + } catch (err2) { + done(err2); + } }; var strategy = new MultiSamlStrategy({ @@ -48,10 +52,14 @@ describe('strategy#authenticate', function() { passReqToCallback: true, authnRequestBinding: 'HTTP-POST', getSamlOptions: function (req, fn) { - fn(); - strategy._passReqToCallback.should.eql(true); - strategy._authnRequestBinding.should.eql('HTTP-POST'); - done(); + try { + fn(); + strategy._passReqToCallback.should.eql(true); + strategy._authnRequestBinding.should.eql('HTTP-POST'); + done(); + } catch (err2) { + done(err2); + } } }; @@ -74,12 +82,16 @@ describe('strategy#authenticate', function() { }; function getSamlOptions (req, fn) { - fn(null, samlOptions); - strategy._saml.options.should.containEql(Object.assign({}, - { cacheProvider: 'mock cache provider' }, - samlOptions - )); - done(); + try { + fn(null, samlOptions); + strategy._saml.options.should.containEql(Object.assign({}, + { cacheProvider: 'mock cache provider' }, + samlOptions + )); + done(); + } catch (err2) { + done(err2); + } } var strategy = new MultiSamlStrategy( @@ -102,9 +114,13 @@ describe('strategy#logout', function() { it('calls super with request and auth options', function(done) { var superAuthenticateStub = this.superAuthenticateStub; function getSamlOptions (req, fn) { - fn(); - sinon.assert.calledOnce(superAuthenticateStub); - done(); + try { + fn(); + sinon.assert.calledOnce(superAuthenticateStub); + done(); + } catch (err2) { + done(err2); + } }; var strategy = new MultiSamlStrategy({ getSamlOptions: getSamlOptions }, verify); @@ -116,10 +132,14 @@ describe('strategy#logout', function() { passReqToCallback: true, authnRequestBinding: 'HTTP-POST', getSamlOptions: function (req, fn) { - fn(); - strategy._passReqToCallback.should.eql(true); - strategy._authnRequestBinding.should.eql('HTTP-POST'); - done(); + try { + fn(); + strategy._passReqToCallback.should.eql(true); + strategy._authnRequestBinding.should.eql('HTTP-POST'); + done(); + } catch (err2) { + done(err2); + } } }; @@ -142,9 +162,13 @@ describe('strategy#logout', function() { }; function getSamlOptions (req, fn) { - fn(null, samlOptions); - strategy._saml.options.should.containEql(samlOptions); - done(); + try { + fn(null, samlOptions); + strategy._saml.options.should.containEql(samlOptions); + done(); + } catch (err2) { + done(err2); + } } var strategy = new MultiSamlStrategy( @@ -167,11 +191,15 @@ describe('strategy#generateServiceProviderMetadata', function() { it('calls super with request and generateServiceProviderMetadata options', function(done) { var superGenerateServiceProviderMetadata = this.superGenerateServiceProviderMetadata; function getSamlOptions (req, fn) { - fn(); - sinon.assert.calledOnce(superGenerateServiceProviderMetadata); - superGenerateServiceProviderMetadata.calledWith('bar', 'baz'); - req.should.eql('foo'); - done(); + try { + fn(); + sinon.assert.calledOnce(superGenerateServiceProviderMetadata); + superGenerateServiceProviderMetadata.calledWith('bar', 'baz'); + req.should.eql('foo'); + done(); + } catch (err2) { + done(err2); + } }; @@ -184,10 +212,14 @@ describe('strategy#generateServiceProviderMetadata', function() { passReqToCallback: true, authnRequestBinding: 'HTTP-POST', getSamlOptions: function (req, fn) { - fn(); - strategy._passReqToCallback.should.eql(true); - strategy._authnRequestBinding.should.eql('HTTP-POST'); - done(); + try { + fn(); + strategy._passReqToCallback.should.eql(true); + strategy._authnRequestBinding.should.eql('HTTP-POST'); + done(); + } catch (err2) { + done(err2); + } } }; @@ -204,8 +236,12 @@ describe('strategy#generateServiceProviderMetadata', function() { var strategy = new MultiSamlStrategy(passportOptions, verify); strategy.generateServiceProviderMetadata('foo', 'bar', 'baz', function (error, result) { - should(error).equal('My error'); - done(); + try { + should(error).equal('My error'); + done(); + } catch (err2) { + done(err2); + } }); }); @@ -218,8 +254,12 @@ describe('strategy#generateServiceProviderMetadata', function() { var strategy = new MultiSamlStrategy(passportOptions, verify); strategy.generateServiceProviderMetadata('foo', 'bar', 'baz', function (error, result) { - should(result).equal('My Metadata Result'); - done(); + try { + should(result).equal('My Metadata Result'); + done(); + } catch (err2) { + done(err2); + } }); }); }); diff --git a/test/samlTests.js b/test/samlTests.js index 3a44cce6..8ea66ba7 100644 --- a/test/samlTests.js +++ b/test/samlTests.js @@ -35,42 +35,66 @@ describe('SAML.js', function () { describe('getAuthorizeUrl', function () { it('calls callback with right host', function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { - url.parse(target).host.should.equal('exampleidp.com'); - done(); + try { + url.parse(target).host.should.equal('exampleidp.com'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with right protocol', function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { - url.parse(target).protocol.should.equal('https:'); - done(); + try { + url.parse(target).protocol.should.equal('https:'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with right path', function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { - url.parse(target).pathname.should.equal('/path'); - done(); + try { + url.parse(target).pathname.should.equal('/path'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with original query string', function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { - url.parse(target, true).query['key'].should.equal('value'); - done(); + try { + url.parse(target, true).query['key'].should.equal('value'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with additional run-time params in query string', function (done) { saml.getAuthorizeUrl(req, options, function (err, target) { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query['key'].should.equal('value'); - url.parse(target, true).query['SAMLRequest'].should.not.be.empty(); - url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); - done(); + try { + Object.keys(url.parse(target, true).query).should.have.length(3); + url.parse(target, true).query['key'].should.equal('value'); + url.parse(target, true).query['SAMLRequest'].should.not.be.empty(); + url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); + done(); + } catch (err2) { + done(err2); + } }); }); // NOTE: This test only tests existence of the assertion, not the correctness it('calls callback with saml request object', function (done) { saml.getAuthorizeUrl(req, {}, function (err, target) { - should(url.parse(target, true).query).have.property('SAMLRequest'); - done(); + try { + should(url.parse(target, true).query).have.property('SAMLRequest'); + done(); + } catch (err2) { + done(err2); + } }); }); }); @@ -78,42 +102,66 @@ describe('SAML.js', function () { describe('getLogoutUrl', function () { it('calls callback with right host', function (done) { saml.getLogoutUrl(req, {}, function (err, target) { - url.parse(target).host.should.equal('exampleidp.com'); - done(); + try { + url.parse(target).host.should.equal('exampleidp.com'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with right protocol', function (done) { saml.getLogoutUrl(req, {}, function (err, target) { - url.parse(target).protocol.should.equal('https:'); - done(); + try { + url.parse(target).protocol.should.equal('https:'); + done(); + } catch(err2) { + done(err2); + } }); }); it('calls callback with right path', function (done) { saml.getLogoutUrl(req, {}, function (err, target) { - url.parse(target).pathname.should.equal('/path'); - done(); + try { + url.parse(target).pathname.should.equal('/path'); + done(); + } catch(err2) { + done(err2); + } }); }); it('calls callback with original query string', function (done) { saml.getLogoutUrl(req, {}, function (err, target) { - url.parse(target, true).query['key'].should.equal('value'); - done(); + try { + url.parse(target, true).query['key'].should.equal('value'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with additional run-time params in query string', function (done) { saml.getLogoutUrl(req, options, function (err, target) { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query['key'].should.equal('value'); - url.parse(target, true).query['SAMLRequest'].should.not.be.empty(); - url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); - done(); + try { + Object.keys(url.parse(target, true).query).should.have.length(3); + url.parse(target, true).query['key'].should.equal('value'); + url.parse(target, true).query['SAMLRequest'].should.not.be.empty(); + url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); + done(); + } catch (err2) { + done(err2); + } }); }); // NOTE: This test only tests existence of the assertion, not the correctness it('calls callback with saml request object', function (done) { saml.getLogoutUrl(req, {}, function (err, target) { - should(url.parse(target, true).query).have.property('SAMLRequest'); - done(); + try { + should(url.parse(target, true).query).have.property('SAMLRequest'); + done(); + } catch (err2) { + done(err2); + } }); }); }); @@ -121,42 +169,66 @@ describe('SAML.js', function () { describe('getLogoutResponseUrl', function () { it('calls callback with right host', function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { - url.parse(target).host.should.equal('exampleidp.com'); - done(); + try { + url.parse(target).host.should.equal('exampleidp.com'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with right protocol', function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { - url.parse(target).protocol.should.equal('https:'); - done(); + try { + url.parse(target).protocol.should.equal('https:'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with right path', function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { - url.parse(target).pathname.should.equal('/path'); - done(); + try { + url.parse(target).pathname.should.equal('/path'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with original query string', function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { - url.parse(target, true).query['key'].should.equal('value'); - done(); + try { + url.parse(target, true).query['key'].should.equal('value'); + done(); + } catch (err2) { + done(err2); + } }); }); it('calls callback with additional run-time params in query string', function (done) { saml.getLogoutResponseUrl(req, options, function (err, target) { - Object.keys(url.parse(target, true).query).should.have.length(3); - url.parse(target, true).query['key'].should.equal('value'); - url.parse(target, true).query['SAMLResponse'].should.not.be.empty(); - url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); - done(); + try { + Object.keys(url.parse(target, true).query).should.have.length(3); + url.parse(target, true).query['key'].should.equal('value'); + url.parse(target, true).query['SAMLResponse'].should.not.be.empty(); + url.parse(target, true).query['additionalKey'].should.equal('additionalValue'); + done(); + } catch (err2) { + done(err2); + } }); }); // NOTE: This test only tests existence of the assertion, not the correctness it('calls callback with saml response object', function (done) { saml.getLogoutResponseUrl(req, {}, function (err, target) { - should(url.parse(target, true).query).have.property('SAMLResponse'); - done(); + try { + should(url.parse(target, true).query).have.property('SAMLResponse'); + done(); + } catch (err2) { + done(err2); + } }); }); }); diff --git a/test/tests.js b/test/tests.js index eccb5dad..5c35abd2 100644 --- a/test/tests.js +++ b/test/tests.js @@ -129,14 +129,18 @@ describe( 'passport-saml /', function() { }; request(requestOpts, function (err, response, body) { - should.not.exist(err); - response.statusCode.should.equal(check.expectedStatusCode); - if (response.statusCode == 200) { - userSerialized.should.be.true; - if (check.expectedNameIDStartsWith) - profile.nameID.should.startWith(check.expectedNameIDStartsWith); + try { + should.not.exist(err); + response.statusCode.should.equal(check.expectedStatusCode); + if (response.statusCode == 200) { + userSerialized.should.be.true; + if (check.expectedNameIDStartsWith) + profile.nameID.should.startWith(check.expectedNameIDStartsWith); + } + done(); + } catch (err2) { + done(err2); } - done(); }); }); }; @@ -177,17 +181,21 @@ describe( 'passport-saml /', function() { form: check.samlResponse }; request(requestOpts, function (err, response, body) { - should.not.exist(err); - response.statusCode.should.equal(check.expectedStatusCode); - if (response.statusCode == 200) { - should.exist(passedRequest); - passedRequest.url.should.eql('/login'); - passedRequest.method.should.eql('POST'); - should(passedRequest.body).match(check.samlResponse); - } else { - should.not.exist(passedRequest); + try { + should.not.exist(err); + response.statusCode.should.equal(check.expectedStatusCode); + if (response.statusCode == 200) { + should.exist(passedRequest); + passedRequest.url.should.eql('/login'); + passedRequest.method.should.eql('POST'); + should(passedRequest.body).match(check.samlResponse); + } else { + should.not.exist(passedRequest); + } + done(); + } catch (err2) { + done(err2); } - done(); }); }); }; @@ -514,34 +522,46 @@ describe( 'passport-saml /', function() { }; request(requestOpts, function(err, response, body) { - should.not.exist(err); - - var encodedSamlRequest; - if ( check.config.authnRequestBinding === "HTTP-POST" ) { - response.statusCode.should.equal(200); - body.should.match(/[^]*/); - encodedSamlRequest = body.match( /[^]*/); + encodedSamlRequest = body.match( / Date: Mon, 29 Apr 2019 14:56:29 -0400 Subject: [PATCH 2/2] Improve code consistency; fix error handling bugs --- multiSamlStrategy.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/multiSamlStrategy.js b/multiSamlStrategy.js index 46f8bdb1..00f4ea75 100644 --- a/multiSamlStrategy.js +++ b/multiSamlStrategy.js @@ -36,29 +36,33 @@ MultiSamlStrategy.prototype.authenticate = function (req, options) { }); }; -MultiSamlStrategy.prototype.logout = function (req, options) { +MultiSamlStrategy.prototype.logout = function (req, callback) { var self = this; this._options.getSamlOptions(req, function (err, samlOptions) { if (err) { - return self.error(err); + return callback(err); } self._saml = new saml.SAML(Object.assign({}, self._options, samlOptions)); - self.constructor.super_.prototype.logout.call(self, req, options); + self.constructor.super_.prototype.logout.call(self, req, callback); }); }; -MultiSamlStrategy.prototype.generateServiceProviderMetadata = function( req, decryptionCert, signingCert, next ) { +MultiSamlStrategy.prototype.generateServiceProviderMetadata = function( req, decryptionCert, signingCert, callback ) { + if (typeof callback !== 'function') { + throw new Error("Metadata can't be provided synchronously for MultiSamlStrategy."); + } + var self = this; return this._options.getSamlOptions(req, function (err, samlOptions) { if (err) { - return next(err); + return callback(err); } - self._saml = new saml.SAML(samlOptions); - return next(null, self.constructor.super_.prototype.generateServiceProviderMetadata.call(self, decryptionCert, signingCert )); + self._saml = new saml.SAML(Object.assign({}, self._options, samlOptions)); + return callback(null, self.constructor.super_.prototype.generateServiceProviderMetadata.call(self, decryptionCert, signingCert )); }); };