From 7b33f8b4c210d86bc7611e1c74b774bed714c8c8 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 3 Oct 2017 14:04:59 +0100 Subject: [PATCH] Unit test TokenAccessController --- .../TokenAccess/TokenAccessController.coffee | 7 +- .../TokenAccessControllerTests.coffee | 461 ++++++++++++++++++ 2 files changed, 463 insertions(+), 5 deletions(-) create mode 100644 services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee diff --git a/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee b/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee index 93800d7991..e26468858c 100644 --- a/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee +++ b/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee @@ -1,11 +1,8 @@ -ProjectGetter = require "../Project/ProjectGetter" ProjectController = require "../Project/ProjectController" -ProjectEditorHandler = require "../Project/ProjectEditorHandler" -UserGetter = require "../User/UserGetter" AuthenticationController = require '../Authentication/AuthenticationController' -logger = require 'logger-sharelatex' TokenAccessHandler = require './TokenAccessHandler' Errors = require '../Errors/Errors' +logger = require 'logger-sharelatex' module.exports = TokenAccessController = @@ -39,7 +36,7 @@ module.exports = TokenAccessController = logger.log {userId, token}, "requesting read-only token access" TokenAccessHandler.findProjectWithReadOnlyToken token, (err, project) -> if err? - logger.err {err, token, user_id: currentUserId}, + logger.err {err, token, userId}, "error getting project by readOnly token" return next(err) if !project? diff --git a/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee new file mode 100644 index 0000000000..b8da5df5af --- /dev/null +++ b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee @@ -0,0 +1,461 @@ +should = require('chai').should() +SandboxedModule = require('sandboxed-module') +assert = require('assert') +path = require('path') +sinon = require('sinon') +modulePath = path.join __dirname, "../../../../app/js/Features/TokenAccess/TokenAccessController" +expect = require("chai").expect +ObjectId = require("mongojs").ObjectId +MockRequest = require('../helpers/MockRequest') +MockResponse = require('../helpers/MockResponse') +Errors = require "../../../../app/js/Features/Errors/Errors.js" + +describe "TokenAccessController", -> + + beforeEach -> + @readOnlyToken = 'somereadonlytoken' + @readAndWriteToken = '42somereadandwritetoken' + @projectId = ObjectId() + @project = + _id: @projectId + publicAccesLevel: 'tokenBased' + tokens: + readOnly: @readOnlyToken + readAndWrite: @readAndWriteToken + @userId = ObjectId() + @TokenAccessController = SandboxedModule.require modulePath, requires: + '../Project/ProjectController': @ProjectController = {} + '../Authentication/AuthenticationController': @AuthenticationController = {} + './TokenAccessHandler': @TokenAccessHandler = {} + 'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()} + + @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString()) + + + describe 'readAndWriteToken', -> + beforeEach -> + + describe 'when all goes well', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_and_write_token'] = @readAndWriteToken + @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readAndWriteToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken)) + .to.equal true + done() + + it 'should add the user to the project with read-write access', (done) -> + expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount) + .to.equal 1 + expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith( + @userId.toString(), @projectId + )) + .to.equal true + done() + + it 'should pass control to loadEditor', (done) -> + expect(@req.params.Project_id).to.equal @projectId.toString() + expect(@ProjectController.loadEditor.callCount).to.equal 1 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true + done() + + describe 'when findProject produces an error', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_and_write_token'] = @readAndWriteToken + @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub() + .callsArgWith(1, new Error('woops')) + @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readAndWriteToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken)) + .to.equal true + done() + + it 'should not add the user to the project with read-write access', (done) -> + expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with an error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + describe 'when findProject does not find a project', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_and_write_token'] = @readAndWriteToken + @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub() + .callsArgWith(1, null, null) + @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readAndWriteToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken)) + .to.equal true + done() + + it 'should not add the user to the project with read-write access', (done) -> + expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with a not-found error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + describe 'when adding user to project produces an error', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_and_write_token'] = @readAndWriteToken + @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub() + .callsArgWith(2, new Error('woops')) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readAndWriteToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken)) + .to.equal true + done() + + it 'should add the user to the project with read-write access', (done) -> + expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount) + .to.equal 1 + expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith( + @userId.toString(), @projectId + )) + .to.equal true + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with an error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + + describe 'readOnlyToken', -> + beforeEach -> + + describe 'with a user', -> + beforeEach -> + @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString()) + + describe 'when all goes well', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 1 + expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith( + @userId.toString(), @projectId + )) + .to.equal true + done() + + it 'should pass control to loadEditor', (done) -> + expect(@req.params.Project_id).to.equal @projectId.toString() + expect(@ProjectController.loadEditor.callCount).to.equal 1 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true + done() + + describe 'when findProject produces an error', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, new Error('woops')) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should not add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with an error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + describe 'when findProject does not find a project', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, null, null) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should not add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with a not-found error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + describe 'when adding user to project produces an error', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, new Error('woops')) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 1 + expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith( + @userId.toString(), @projectId + )) + .to.equal true + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with an error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + + + describe 'anonymous', -> + beforeEach -> + @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null) + @TokenAccessHandler.grantSessionReadOnlyTokenAccess = sinon.stub() + + describe 'when all goes well', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should give the user session read-only access', (done) -> + expect(@TokenAccessHandler.grantSessionReadOnlyTokenAccess.callCount) + .to.equal 1 + expect(@TokenAccessHandler.grantSessionReadOnlyTokenAccess.calledWith( + @req, @projectId, @readOnlyToken + )) + .to.equal true + done() + + it 'should not add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 0 + done() + + it 'should pass control to loadEditor', (done) -> + expect(@req.params.Project_id).to.equal @projectId.toString() + expect(@req._anonToken).to.equal @readOnlyToken + expect(@ProjectController.loadEditor.callCount).to.equal 1 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true + done() + + describe 'when findProject produces an error', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, new Error('woops')) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should not give the user session read-only access', (done) -> + expect(@TokenAccessHandler.grantSessionReadOnlyTokenAccess.callCount) + .to.equal 0 + done() + + it 'should not add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should call next with an error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() + + describe 'when findProject does not find a project', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @next = sinon.stub() + @req.params['read_only_token'] = @readOnlyToken + @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub() + .callsArgWith(1, null, null) + @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readOnlyToken @req, @res, @next + + it 'should try to find a project with this token', (done) -> + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount) + .to.equal 1 + expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken)) + .to.equal true + done() + + it 'should not give the user session read-only access', (done) -> + expect(@TokenAccessHandler.grantSessionReadOnlyTokenAccess.callCount) + .to.equal 0 + done() + + it 'should not pass control to loadEditor', (done) -> + expect(@ProjectController.loadEditor.callCount).to.equal 0 + expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false + done() + + it 'should not add the user to the project with read-only access', (done) -> + expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount) + .to.equal 0 + done() + + it 'should call next with a not-found error', (done) -> + expect(@next.callCount).to.equal 1 + expect(@next.lastCall.args[0]).to.be.instanceof Error + done() +