From ad999a72b642414edee4cd1bfc44ae685558238b Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Mon, 16 Oct 2017 13:20:15 +0100 Subject: [PATCH] If a token-based project not found, check private overleaf project --- .../TokenAccess/TokenAccessController.coffee | 32 +++-- .../TokenAccess/TokenAccessHandler.coffee | 7 ++ .../TokenAccessControllerTests.coffee | 117 +++++++++++++----- .../TokenAccessHandlerTests.coffee | 33 +++++ 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee b/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee index e26468858c..682b926a0b 100644 --- a/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee +++ b/services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee @@ -19,16 +19,28 @@ module.exports = TokenAccessController = if !project? logger.log {token, userId}, "no project found for readAndWrite token" - return next(new Errors.NotFoundError()) - logger.log {userId, projectId: project._id}, - "adding user to project with readAndWrite token" - TokenAccessHandler.addReadAndWriteUserToProject userId, project._id, (err) -> - if err? - logger.err {err, token, userId, projectId: project._id}, - "error adding user to project with readAndWrite token" - return next(err) - req.params.Project_id = project._id.toString() - return ProjectController.loadEditor(req, res, next) + TokenAccessHandler + .findPrivateOverleafProjectWithReadAndWriteToken token, (err, project) -> + if err? + logger.err {err, token, userId}, + "error getting project by readAndWrite token" + return next(err) + if !project? + logger.log {token, userId}, + "no private-overleaf project found with readAndWriteToken" + return next(new Errors.NotFoundError()) + logger.log {token, projectId: project._id}, "redirecting user to project" + res.redirect(302, "/project/#{project._id}") + else + logger.log {userId, projectId: project._id}, + "adding user to project with readAndWrite token" + TokenAccessHandler.addReadAndWriteUserToProject userId, project._id, (err) -> + if err? + logger.err {err, token, userId, projectId: project._id}, + "error adding user to project with readAndWrite token" + return next(err) + req.params.Project_id = project._id.toString() + return ProjectController.loadEditor(req, res, next) readOnlyToken: (req, res, next) -> userId = AuthenticationController.getLoggedInUserId(req) diff --git a/services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee b/services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee index da6ab55207..8275b10ae4 100644 --- a/services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee +++ b/services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee @@ -16,6 +16,13 @@ module.exports = TokenAccessHandler = 'publicAccesLevel': PublicAccessLevels.TOKEN_BASED }, {_id: 1, publicAccesLevel: 1}, callback + findPrivateOverleafProjectWithReadAndWriteToken: (token, callback=(err, project)->) -> + Project.findOne { + 'tokens.readAndWrite': token, + 'publicAccesLevel': PublicAccessLevels.PRIVATE, + 'overleaf.id': {'$exists': true} + }, {_id: 1, publicAccesLevel: 1, owner_ref: 1}, callback + addReadOnlyUserToProject: (userId, projectId, callback=(err)->) -> userId = ObjectId(userId.toString()) projectId = ObjectId(projectId.toString()) diff --git a/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee index b8da5df5af..31d1d70094 100644 --- a/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessControllerTests.coffee @@ -107,38 +107,99 @@ describe "TokenAccessController", -> 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() + describe 'when it is a private overleaf project', -> + beforeEach -> + @req = new MockRequest() + @res = new MockResponse() + @res.redirect = sinon.stub() + @next = sinon.stub() + @req.params['read_and_write_token'] = @readAndWriteToken + @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub() + .callsArgWith(1, null, null) + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken = + sinon.stub() + .callsArgWith(1, null, @project) + @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub() + .callsArgWith(2, null) + @ProjectController.loadEditor = sinon.stub() + @TokenAccessController.readAndWriteToken @req, @res, @next - it 'should not add the user to the project with read-write access', (done) -> - expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount) - .to.equal 0 - done() + 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 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 try to find a private overleaf project', (done) -> + expect( + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken + .callCount + ).to.equal 1 + expect( + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken + .calledWith(@readAndWriteToken) + ).to.equal true + 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() + 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 not call next with a not-found error', (done) -> + expect(@next.callCount).to.equal 0 + done() + + it 'should redirect to the canonical project url', (done) -> + expect(@res.redirect.callCount).to.equal 1 + expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true + done() + + describe 'when it is not a private overleaf 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.findPrivateOverleafProjectWithReadAndWriteToken = + 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 -> diff --git a/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessHandlerTests.coffee b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessHandlerTests.coffee index 6699b1dd4e..eaf1d02121 100644 --- a/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/TokenAccess/TokenAccessHandlerTests.coffee @@ -84,6 +84,39 @@ describe "TokenAccessHandler", -> done() + describe 'findPrivateOverleafProjectWithReadAndWriteToken', -> + beforeEach -> + @Project.findOne = sinon.stub().callsArgWith(2, null, @project) + + it 'should call Project.findOne', (done) -> + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) => + expect(@Project.findOne.callCount).to.equal 1 + expect(@Project.findOne.calledWith({ + 'tokens.readAndWrite': @token, + 'publicAccesLevel': 'private', + 'overleaf.id': {$exists: true} + })).to.equal true + done() + + it 'should produce a project object with no error', (done) -> + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) => + expect(err).to.not.exist + expect(project).to.exist + expect(project).to.deep.equal @project + done() + + describe 'when Project.findOne produces an error', -> + beforeEach -> + @Project.findOne = sinon.stub().callsArgWith(2, new Error('woops')) + + it 'should produce an error', (done) -> + @TokenAccessHandler.findPrivateOverleafProjectWithReadAndWriteToken @token, (err, project) => + expect(err).to.exist + expect(project).to.not.exist + expect(err).to.be.instanceof Error + done() + + describe 'addReadOnlyUserToProject', -> beforeEach -> @Project.update = sinon.stub().callsArgWith(2, null)