diff --git a/services/web/app/coffee/Features/SudoMode/SudoModeController.coffee b/services/web/app/coffee/Features/SudoMode/SudoModeController.coffee index 8938088cd1..aca5315cbb 100644 --- a/services/web/app/coffee/Features/SudoMode/SudoModeController.coffee +++ b/services/web/app/coffee/Features/SudoMode/SudoModeController.coffee @@ -32,6 +32,10 @@ module.exports = SudoModeController = if err? logger.err {err, userId}, "[SudoMode] error getting user" return next(err) + if !userRecord? + err = new Error('user not found') + logger.err {err, userId}, "[SudoMode] user not found" + return next(err) AuthenticationManager.authenticate email: userRecord.email, password, (err, user) -> if err? logger.err {err, userId}, "[SudoMode] error authenticating user" diff --git a/services/web/test/UnitTests/coffee/SudoMode/SudoModeControllerTests.coffee b/services/web/test/UnitTests/coffee/SudoMode/SudoModeControllerTests.coffee index 5f7683cab3..1870ba1d76 100644 --- a/services/web/test/UnitTests/coffee/SudoMode/SudoModeControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/SudoMode/SudoModeControllerTests.coffee @@ -18,6 +18,7 @@ describe 'SudoModeController', -> activateSudoMode: sinon.stub() @AuthenticationController = getLoggedInUserId: sinon.stub().returns(@user._id) + _getRediretFromSession: sinon.stub() @AuthenticationManager = authenticate: sinon.stub() @UserGetter = @@ -68,3 +69,208 @@ describe 'SudoModeController', -> @SudoModeController.sudoModePrompt(@req, @res, @next) @next.callCount.should.equal 1 expect(@next.lastCall.args[0]).to.be.instanceof Error + + describe 'submitPassword', -> + beforeEach -> + @AuthenticationController._getRedirectFromSession = sinon.stub().returns '/somewhere' + @UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user) + @AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, null, @user) + @SudoModeHandler.activateSudoMode = sinon.stub().callsArgWith(1, null) + @password = 'a_terrible_secret' + @req = {body: {password: @password}} + @res = {json: sinon.stub()} + @next = sinon.stub() + + describe 'when all goes well', -> + beforeEach -> + + it 'should get the logged in user id', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationController.getLoggedInUserId.callCount.should.equal 1 + @AuthenticationController.getLoggedInUserId.calledWith(@req).should.equal true + + it 'should get redirect from session', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationController._getRedirectFromSession.callCount.should.equal 1 + @AuthenticationController._getRedirectFromSession.calledWith(@req).should.equal true + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 1 + @AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true + + it 'should activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 1 + @SudoModeHandler.activateSudoMode.calledWith(@user._id).should.equal true + + it 'should send back a json response', -> + @SudoModeController.submitPassword(@req, @res, @next) + @res.json.callCount.should.equal 1 + @res.json.calledWith({redir: '/somewhere'}).should.equal true + + it 'should not call next', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 0 + + describe 'when no password is supplied', -> + beforeEach -> + @req.body.password = '' + @next = sinon.stub() + + it 'should return next with an error', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + + it 'should not get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 0 + + it 'should not try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 0 + + it 'should not activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 0 + + it 'should not send back a json response', -> + @SudoModeController.submitPassword(@req, @res, @next) + @res.json.callCount.should.equal 0 + + describe 'when getUser produces an error', -> + beforeEach -> + @UserGetter.getUser = sinon.stub().callsArgWith(2, new Error('woops')) + @next = sinon.stub() + + it 'should return next with an error', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should not try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 0 + + it 'should not activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 0 + + it 'should not send back a json response', -> + @SudoModeController.submitPassword(@req, @res, @next) + @res.json.callCount.should.equal 0 + + describe 'when getUser does not find a user', -> + beforeEach -> + @UserGetter.getUser = sinon.stub().callsArgWith(2, null, null) + @next = sinon.stub() + + it 'should return next with an error', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should not try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 0 + + it 'should not activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 0 + + it 'should not send back a json response', -> + @SudoModeController.submitPassword(@req, @res, @next) + @res.json.callCount.should.equal 0 + + describe 'when authentication fails', -> + beforeEach -> + @AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, null, null) + @res.json = sinon.stub() + @req.i18n = {translate: sinon.stub()} + + it 'should send back a failure message', -> + @SudoModeController.submitPassword(@req, @res, @next) + @res.json.callCount.should.equal 1 + expect(@res.json.lastCall.args[0]).to.have.keys ['message'] + expect(@res.json.lastCall.args[0].message).to.have.keys ['text', 'type'] + @req.i18n.translate.callCount.should.equal 1 + @req.i18n.translate.calledWith('invalid_password') + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 1 + @AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true + + it 'should not activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 0 + + describe 'when authentication produces an error', -> + beforeEach -> + @AuthenticationManager.authenticate = sinon.stub().callsArgWith(2, new Error('woops')) + @next = sinon.stub() + + it 'should return next with an error', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 1 + @AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true + + it 'should not activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 0 + + describe 'when sudo mode activation produces an error', -> + beforeEach -> + @SudoModeHandler.activateSudoMode = sinon.stub().callsArgWith(1, new Error('woops')) + @next = sinon.stub() + + it 'should return next with an error', -> + @SudoModeController.submitPassword(@req, @res, @next) + @next.callCount.should.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + + it 'should get the user from storage', -> + @SudoModeController.submitPassword(@req, @res, @next) + @UserGetter.getUser.callCount.should.equal 1 + @UserGetter.getUser.calledWith('some_object_id', {email: 1}).should.equal true + + it 'should try to authenticate the user with the password', -> + @SudoModeController.submitPassword(@req, @res, @next) + @AuthenticationManager.authenticate.callCount.should.equal 1 + @AuthenticationManager.authenticate.calledWith({email: @user.email}, @password).should.equal true + + it 'should have tried to activate sudo mode', -> + @SudoModeController.submitPassword(@req, @res, @next) + @SudoModeHandler.activateSudoMode.callCount.should.equal 1 + @SudoModeHandler.activateSudoMode.calledWith(@user._id).should.equal true