overleaf/services/web/test/unit/coffee/TokenAccess/TokenAccessControllerTests.coffee

1222 lines
42 KiB
CoffeeScript
Raw Normal View History

2017-10-03 09:04:59 -04:00
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()
@ownerId = 'owner'
2017-10-03 09:04:59 -04:00
@project =
_id: @projectId
publicAccesLevel: 'tokenBased'
tokens:
readOnly: @readOnlyToken
readAndWrite: @readAndWriteToken
owner_ref: @ownerId
2017-10-03 09:04:59 -04:00
@userId = ObjectId()
@TokenAccessController = SandboxedModule.require modulePath, requires:
'../Project/ProjectController': @ProjectController = {}
'../Authentication/AuthenticationController': @AuthenticationController = {}
2018-10-02 11:16:46 -04:00
'./TokenAccessHandler': @TokenAccessHandler = {
getV1DocPublishedInfo: sinon.stub().yields(null, {
2018-10-02 11:16:46 -04:00
allow: true
})
getV1DocInfo: sinon.stub().yields(null, {
2018-10-02 11:16:46 -04:00
exists: true
exported: false
})
}
'../../infrastructure/Features': @Features = {
hasFeature: sinon.stub().returns(false)
}
2017-10-03 09:04:59 -04:00
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
'settings-sharelatex': {
overleaf:
host: 'http://overleaf.test:5000'
}
'../V1/V1Api': @V1Api = {
request: sinon.stub().callsArgWith(1, null, {}, { allow: true })
}
2017-10-03 09:04:59 -04:00
@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, true)
2017-10-03 09:04:59 -04:00
@TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
.callsArgWith(2, null)
@ProjectController.loadEditor = sinon.stub()
@AuthenticationController.setRedirectInSession = sinon.stub()
2017-10-03 09:04:59 -04:00
@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 the user is already the owner', ->
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@next = sinon.stub()
@req.params['read_and_write_token'] = @readAndWriteToken
@project.owner_ref = @userId
@TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
.callsArgWith(1, null, @project, true)
@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 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()
2017-10-03 09:04:59 -04:00
describe 'when there is no user', ->
2017-10-03 09:04:59 -04:00
beforeEach ->
@AuthenticationController.getLoggedInUserId =
sinon.stub().returns(null)
2017-10-03 09:04:59 -04:00
describe 'when anonymous read-write access is enabled', ->
beforeEach ->
@TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = true
@req = new MockRequest()
@res = new MockResponse()
@next = sinon.stub()
@req.params['read_and_write_token'] = @readAndWriteToken
@TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
.callsArgWith(1, null, @project, true)
@TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
.callsArgWith(2, null)
@ProjectController.loadEditor = sinon.stub()
@TokenAccessHandler.grantSessionTokenAccess = 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 give the user session token access', (done) ->
expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
.to.equal 1
expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
@req, @projectId, @readAndWriteToken
))
.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 anonymous read-write access is not enabled', ->
beforeEach ->
@TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = false
@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, @project, true)
@TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
.callsArgWith(2, null)
@ProjectController.loadEditor = sinon.stub()
@TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
@AuthenticationController.setRedirectInSession = 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 give the user session token access', (done) ->
expect(@TokenAccessHandler.grantSessionTokenAccess.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 set redirect in session', (done) ->
expect(@AuthenticationController.setRedirectInSession.callCount).to.equal 1
expect(@AuthenticationController.setRedirectInSession.calledWith(@req)).to.equal true
done()
it 'should redirect to restricted page', (done) ->
expect(@res.redirect.callCount).to.equal 1
expect(@res.redirect.calledWith('/restricted')).to.equal true
done()
2017-10-03 09:04:59 -04:00
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 ->
describe 'when user is present', ->
beforeEach ->
@AuthenticationController.getLoggedInUserId =
sinon.stub().returns(@userId.toString())
describe 'when project does not exist', ->
beforeEach ->
@req = new MockRequest()
@req.url = '/123abc'
@res = new MockResponse()
@res.redirect = sinon.stub()
@res.render = sinon.stub()
@next = sinon.stub()
@req.params['read_and_write_token'] = '123abc'
@TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
.callsArgWith(1, null, null, false)
describe 'when project was not exported from v1', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should redirect to v1', (done) ->
expect(@res.redirect.callCount).to.equal 1
expect(@res.redirect.calledWith(
302,
'/sign_in_to_v1?return_to=/123abc'
)).to.equal true
done()
describe 'when project was not exported from v1 but forcing import to v2', ->
beforeEach ->
@Features.hasFeature.returns(true)
describe 'with project name', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: true
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render v2-import page with name', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: '123abc'
name: 'A title'
hasOwner: true
hasAssignment: false
brandInfo: null
}
)).to.equal true
done()
describe 'with project owner', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: true
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: '123abc'
hasOwner: true
name: 'A title'
hasAssignment: false
brandInfo: null
}
)).to.equal true
done()
describe 'without project owner', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: '123abc',
hasOwner: false
name: 'A title',
hasAssignment: false,
brandInfo: null
}
)).to.equal true
done()
describe 'with assignment', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: true
brand_info: null
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: '123abc',
hasOwner: false
name: 'A title',
hasAssignment: true,
brandInfo: null
}
)).to.equal true
done()
describe 'with brand info', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: false
brand_info: 'wellcome'
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: '123abc',
hasOwner: false
name: 'A title',
hasAssignment: false,
brandInfo: 'wellcome'
}
)).to.equal true
done()
describe 'with anonymous user', ->
beforeEach ->
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should render anonymous import status page', (done) ->
expect(@res.render.callCount).to.equal 1
expect(@res.render.calledWith(
'project/v2-import',
{ loginRedirect: '/123abc' }
)).to.equal true
done()
describe 'when project was exported from v1', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: true
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should call next with a not-found error', (done) ->
expect(@next.callCount).to.equal 1
done()
describe 'when project does not exist on v1', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: false
exported: false
})
@TokenAccessController.readAndWriteToken @req, @res, @next
it 'should call next with a not-found error', (done) ->
expect(@next.callCount).to.equal 1
expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
done()
describe 'when token access is off, but user has higher access anyway', ->
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, true)
@TokenAccessHandler.findProjectWithHigherAccess =
sinon.stub()
.callsArgWith(2, 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 check if user has higher access to the token project', (done) ->
expect(
@TokenAccessHandler.findProjectWithHigherAccess.callCount
).to.equal 1
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 higher access is not available', ->
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, true)
@TokenAccessHandler.findProjectWithHigherAccess =
sinon.stub()
.callsArgWith(2, 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 check if user has higher access to the token project', (done) ->
expect(
@TokenAccessHandler.findProjectWithHigherAccess.callCount
).to.equal 1
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()
2017-10-03 09:04:59 -04:00
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, true)
2017-10-03 09:04:59 -04:00
@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 ->
@TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, true)
2017-10-03 09:04:59 -04:00
describe 'when access not allowed by v1 api', ->
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@res.redirect = sinon.stub()
@next = sinon.stub()
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
2018-09-25 08:54:01 -04:00
.callsArgWith(1, null, @project, true)
@TokenAccessHandler.getV1DocPublishedInfo = sinon.stub().yields(null, {
2018-10-02 11:16:46 -04:00
allow: false
published_path: 'doc-url'
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should redirect to doc-url', ->
expect(@res.redirect.calledWith('doc-url')).to.equal true
2017-10-03 09:04:59 -04:00
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, true)
2017-10-03 09:04:59 -04:00
@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 the user is already the owner', ->
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@next = sinon.stub()
@req.params['read_only_token'] = @readOnlyToken
@project.owner_ref = @userId
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
.callsArgWith(1, null, @project, true)
@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 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()
2017-10-03 09:04:59 -04:00
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', ->
describe 'when project does not exist', ->
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@res.redirect = sinon.stub()
@next = sinon.stub()
@req.params['read_only_token'] = 'abcd'
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
.callsArgWith(1, null, null, false)
@TokenAccessHandler.checkV1ProjectExported = sinon.stub()
.callsArgWith(1, null, false)
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should redirect to v1', (done) ->
expect(@res.redirect.callCount).to.equal 1
expect(@res.redirect.calledWith(
302,
'/sign_in_to_v1?return_to=/read/abcd'
)).to.equal true
done()
describe 'when project was not exported from v1 but forcing import to v2', ->
beforeEach ->
@Features.hasFeature.returns(true)
@req = new MockRequest()
@res = new MockResponse()
@res.render = sinon.stub()
@next = sinon.stub()
@req.params['read_only_token'] = 'abcd'
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
.callsArgWith(1, null, null, false)
describe 'with project name', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: true
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render v2-import page with name', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: 'abcd'
name: 'A title'
hasOwner: true
hasAssignment: false
brandInfo: null
}
)).to.equal true
done()
describe 'with project owner', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: true
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: 'abcd',
hasOwner: true
name: 'A title'
hasAssignment: false
brandInfo: null
}
)).to.equal true
done()
describe 'without project owner', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: false
brand_info: null
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: 'abcd',
hasOwner: false
name: 'A title'
hasAssignment: false
brandInfo: null
}
)).to.equal true
done()
describe 'with assignment', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: true
brand_info: null
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: 'abcd',
hasOwner: false
name: 'A title'
hasAssignment: true
brandInfo: null
}
)).to.equal true
done()
describe 'with brand info', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
has_owner: false
name: 'A title'
has_assignment: false
brand_info: 'f1000'
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render v2-import page', (done) ->
expect(@res.render.calledWith(
'project/v2-import',
{
projectId: 'abcd',
hasOwner: false
name: 'A title'
hasAssignment: false
brandInfo: 'f1000'
}
)).to.equal true
done()
describe 'when project was exported from v1', ->
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@res.redirect = sinon.stub()
@next = sinon.stub()
@req.params['read_only_token'] = 'abcd'
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
.callsArgWith(1, null, null, false)
2018-10-02 11:16:46 -04:00
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
allow: true
exists: true
exported: true
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should call next with a not-found error', (done) ->
expect(@next.callCount).to.equal 1
done()
describe 'when token access is off, but user has higher access anyway', ->
2017-10-03 09:04:59 -04:00
beforeEach ->
@req = new MockRequest()
@res = new MockResponse()
@res.redirect = sinon.stub()
2017-10-03 09:04:59 -04:00
@next = sinon.stub()
@req.params['read_and_write_token'] = @readAndWriteToken
@TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
.callsArgWith(1, null, null, true)
@TokenAccessHandler.findProjectWithHigherAccess =
sinon.stub()
.callsArgWith(2, null, @project)
@TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
2017-10-03 09:04:59 -04:00
.callsArgWith(2, null)
@ProjectController.loadEditor = sinon.stub()
@TokenAccessController.readAndWriteToken @req, @res, @next
2017-10-03 09:04:59 -04:00
it 'should try to find a project with this token', (done) ->
expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
2017-10-03 09:04:59 -04:00
.to.equal 1
expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
2017-10-03 09:04:59 -04:00
.to.equal true
done()
it 'should check if user has higher access to the token project', (done) ->
expect(
@TokenAccessHandler.findProjectWithHigherAccess.callCount
).to.equal 1
done()
it 'should not add the user to the project with read-write access', (done) ->
expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
2017-10-03 09:04:59 -04:00
.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
2017-10-03 09:04:59 -04:00
done()
describe 'when higher access is not available', ->
2017-10-03 09:04:59 -04:00
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, true)
@TokenAccessHandler.findProjectWithHigherAccess =
sinon.stub()
.callsArgWith(2, null, null)
2017-10-03 09:04:59 -04:00
@TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
.callsArgWith(2, null)
2017-10-03 09:04:59 -04:00
@ProjectController.loadEditor = sinon.stub()
@TokenAccessController.readAndWriteToken @req, @res, @next
2017-10-03 09:04:59 -04:00
it 'should try to find a project with this token', (done) ->
expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
2017-10-03 09:04:59 -04:00
.to.equal 1
expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
@readAndWriteToken
)).to.equal true
2017-10-03 09:04:59 -04:00
done()
it 'should check if user has higher access to the token project', (done) ->
expect(
@TokenAccessHandler.findProjectWithHigherAccess.callCount
).to.equal 1
done()
it 'should not add the user to the project with read-write access', (done) ->
2017-10-03 09:04:59 -04:00
expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
.to.equal 0
2017-10-03 09:04:59 -04:00
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) ->
2017-10-03 09:04:59 -04:00
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, true)
@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()
2017-10-03 09:04:59 -04:00
it 'should call next with an error', (done) ->
expect(@next.callCount).to.equal 1
expect(@next.lastCall.args[0]).to.be.instanceof Error
done()
2017-10-03 09:04:59 -04:00
describe 'anonymous', ->
beforeEach ->
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
@TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
2017-10-03 09:04:59 -04:00
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, true)
2017-10-03 09:04:59 -04:00
@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.grantSessionTokenAccess.callCount)
2017-10-03 09:04:59 -04:00
.to.equal 1
expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
2017-10-03 09:04:59 -04:00
@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._anonymousAccessToken).to.equal @readOnlyToken
2017-10-03 09:04:59 -04:00
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.grantSessionTokenAccess.callCount)
2017-10-03 09:04:59 -04:00
.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()
@res.redirect = sinon.stub()
2017-10-03 09:04:59 -04:00
@next = sinon.stub()
@req.params['read_only_token'] = @readOnlyToken
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
2017-10-03 09:04:59 -04:00
@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
.callsArgWith(1, null, false)
2017-10-03 09:04:59 -04:00
@TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
describe 'when project does not exist', ->
beforeEach ->
@TokenAccessController.readOnlyToken @req, @res, @next
2017-10-03 09:04:59 -04:00
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()
2017-10-03 09:04:59 -04:00
it 'should not give the user session read-only access', (done) ->
expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
.to.equal 0
done()
2017-10-03 09:04:59 -04:00
it 'should not add the user to the project with read-only access', (done) ->
expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
.to.equal 0
done()
2017-10-03 09:04:59 -04:00
describe 'when project was exported to v2', ->
beforeEach ->
2018-10-02 11:16:46 -04:00
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: true
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should call next with not found error', (done) ->
expect(@next.callCount).to.equal 1
expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
done()
describe 'when project was not exported to v2', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should redirect to v1', (done) ->
expect(@res.redirect.callCount).to.equal 1
expect(@res.redirect.calledWith(
302,
"/sign_in_to_v1?return_to=/read/#{@readOnlyToken}"
)).to.equal true
done()
2017-10-03 09:04:59 -04:00
describe 'when project does not exist on v1', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: false,
exported: false
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should call next with not found error', (done) ->
expect(@next.callCount).to.equal 1
expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
done()
describe 'anonymous user', ->
beforeEach ->
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
describe 'when project was not exported to v2', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should redirect to v1', (done) ->
expect(@res.redirect.callCount).to.equal 1
expect(@res.redirect.calledWith(
302,
"/sign_in_to_v1?return_to=/read/#{@readOnlyToken}"
)).to.equal true
done()
describe 'force-import-to-v2 flag is on', ->
beforeEach ->
@res.render = sinon.stub()
@Features.hasFeature.returns(true)
describe 'when project was not exported to v2', ->
beforeEach ->
@TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
exists: true
exported: false
})
@TokenAccessController.readOnlyToken @req, @res, @next
it 'should render anonymous import status page', (done) ->
expect(@res.render.callCount).to.equal 1
expect(@res.render.calledWith(
'project/v2-import',
{ loginRedirect: "/read/#{@readOnlyToken}" }
)).to.equal true
done()