TokenAccessControllerTests.coffee 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. should = require('chai').should()
  2. SandboxedModule = require('sandboxed-module')
  3. assert = require('assert')
  4. path = require('path')
  5. sinon = require('sinon')
  6. modulePath = path.join __dirname, "../../../../app/js/Features/TokenAccess/TokenAccessController"
  7. expect = require("chai").expect
  8. ObjectId = require("mongojs").ObjectId
  9. MockRequest = require('../helpers/MockRequest')
  10. MockResponse = require('../helpers/MockResponse')
  11. Errors = require "../../../../app/js/Features/Errors/Errors.js"
  12. describe "TokenAccessController", ->
  13. beforeEach ->
  14. @readOnlyToken = 'somereadonlytoken'
  15. @readAndWriteToken = '42somereadandwritetoken'
  16. @projectId = ObjectId()
  17. @ownerId = 'owner'
  18. @project =
  19. _id: @projectId
  20. publicAccesLevel: 'tokenBased'
  21. tokens:
  22. readOnly: @readOnlyToken
  23. readAndWrite: @readAndWriteToken
  24. owner_ref: @ownerId
  25. @userId = ObjectId()
  26. @TokenAccessController = SandboxedModule.require modulePath, requires:
  27. '../Project/ProjectController': @ProjectController = {}
  28. '../Authentication/AuthenticationController': @AuthenticationController = {}
  29. './TokenAccessHandler': @TokenAccessHandler = {}
  30. 'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
  31. 'settings-sharelatex': {
  32. overleaf:
  33. host: 'http://overleaf.test:5000'
  34. }
  35. '../V1/V1Api': @V1Api = {
  36. request: sinon.stub().callsArgWith(1, null, {}, { allow: true })
  37. }
  38. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  39. describe 'readAndWriteToken', ->
  40. beforeEach ->
  41. describe 'when all goes well', ->
  42. beforeEach ->
  43. @req = new MockRequest()
  44. @res = new MockResponse()
  45. @next = sinon.stub()
  46. @req.params['read_and_write_token'] = @readAndWriteToken
  47. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  48. .callsArgWith(1, null, @project, true)
  49. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  50. .callsArgWith(2, null)
  51. @ProjectController.loadEditor = sinon.stub()
  52. @AuthenticationController._setRedirectInSession = sinon.stub()
  53. @TokenAccessController.readAndWriteToken @req, @res, @next
  54. it 'should try to find a project with this token', (done) ->
  55. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  56. .to.equal 1
  57. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  58. .to.equal true
  59. done()
  60. it 'should add the user to the project with read-write access', (done) ->
  61. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  62. .to.equal 1
  63. expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  64. @userId.toString(), @projectId
  65. ))
  66. .to.equal true
  67. done()
  68. it 'should pass control to loadEditor', (done) ->
  69. expect(@req.params.Project_id).to.equal @projectId.toString()
  70. expect(@ProjectController.loadEditor.callCount).to.equal 1
  71. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  72. done()
  73. describe 'when the user is already the owner', ->
  74. beforeEach ->
  75. @req = new MockRequest()
  76. @res = new MockResponse()
  77. @next = sinon.stub()
  78. @req.params['read_and_write_token'] = @readAndWriteToken
  79. @project.owner_ref = @userId
  80. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  81. .callsArgWith(1, null, @project, true)
  82. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  83. .callsArgWith(2, null)
  84. @ProjectController.loadEditor = sinon.stub()
  85. @TokenAccessController.readAndWriteToken @req, @res, @next
  86. it 'should try to find a project with this token', (done) ->
  87. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  88. .to.equal 1
  89. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  90. .to.equal true
  91. done()
  92. it 'should not add the user to the project with read-write access', (done) ->
  93. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  94. .to.equal 0
  95. done()
  96. it 'should pass control to loadEditor', (done) ->
  97. expect(@req.params.Project_id).to.equal @projectId.toString()
  98. expect(@ProjectController.loadEditor.callCount).to.equal 1
  99. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  100. done()
  101. describe 'when there is no user', ->
  102. beforeEach ->
  103. @AuthenticationController.getLoggedInUserId =
  104. sinon.stub().returns(null)
  105. describe 'when anonymous read-write access is enabled', ->
  106. beforeEach ->
  107. @TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = true
  108. @req = new MockRequest()
  109. @res = new MockResponse()
  110. @next = sinon.stub()
  111. @req.params['read_and_write_token'] = @readAndWriteToken
  112. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  113. .callsArgWith(1, null, @project, true)
  114. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  115. .callsArgWith(2, null)
  116. @ProjectController.loadEditor = sinon.stub()
  117. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  118. @TokenAccessController.readAndWriteToken @req, @res, @next
  119. it 'should not add the user to the project with read-write access', (done) ->
  120. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  121. .to.equal 0
  122. done()
  123. it 'should give the user session token access', (done) ->
  124. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  125. .to.equal 1
  126. expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
  127. @req, @projectId, @readAndWriteToken
  128. ))
  129. .to.equal true
  130. done()
  131. it 'should pass control to loadEditor', (done) ->
  132. expect(@req.params.Project_id).to.equal @projectId.toString()
  133. expect(@ProjectController.loadEditor.callCount).to.equal 1
  134. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  135. done()
  136. describe 'when anonymous read-write access is not enabled', ->
  137. beforeEach ->
  138. @TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = false
  139. @req = new MockRequest()
  140. @res = new MockResponse()
  141. @res.redirect = sinon.stub()
  142. @next = sinon.stub()
  143. @req.params['read_and_write_token'] = @readAndWriteToken
  144. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  145. .callsArgWith(1, null, @project, true)
  146. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  147. .callsArgWith(2, null)
  148. @ProjectController.loadEditor = sinon.stub()
  149. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  150. @AuthenticationController._setRedirectInSession = sinon.stub()
  151. @TokenAccessController.readAndWriteToken @req, @res, @next
  152. it 'should not add the user to the project with read-write access', (done) ->
  153. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  154. .to.equal 0
  155. done()
  156. it 'should give the user session token access', (done) ->
  157. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  158. .to.equal 0
  159. done()
  160. it 'should not pass control to loadEditor', (done) ->
  161. expect(@ProjectController.loadEditor.callCount).to.equal 0
  162. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  163. done()
  164. it 'should set redirect in session', (done) ->
  165. expect(@AuthenticationController._setRedirectInSession.callCount).to.equal 1
  166. expect(@AuthenticationController._setRedirectInSession.calledWith(@req)).to.equal true
  167. done()
  168. it 'should redirect to restricted page', (done) ->
  169. expect(@res.redirect.callCount).to.equal 1
  170. expect(@res.redirect.calledWith('/restricted')).to.equal true
  171. done()
  172. describe 'when findProject produces an error', ->
  173. beforeEach ->
  174. @req = new MockRequest()
  175. @res = new MockResponse()
  176. @next = sinon.stub()
  177. @req.params['read_and_write_token'] = @readAndWriteToken
  178. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  179. .callsArgWith(1, new Error('woops'))
  180. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  181. .callsArgWith(2, null)
  182. @ProjectController.loadEditor = sinon.stub()
  183. @TokenAccessController.readAndWriteToken @req, @res, @next
  184. it 'should try to find a project with this token', (done) ->
  185. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  186. .to.equal 1
  187. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  188. .to.equal true
  189. done()
  190. it 'should not add the user to the project with read-write access', (done) ->
  191. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  192. .to.equal 0
  193. done()
  194. it 'should not pass control to loadEditor', (done) ->
  195. expect(@ProjectController.loadEditor.callCount).to.equal 0
  196. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  197. done()
  198. it 'should call next with an error', (done) ->
  199. expect(@next.callCount).to.equal 1
  200. expect(@next.lastCall.args[0]).to.be.instanceof Error
  201. done()
  202. describe 'when findProject does not find a project', ->
  203. beforeEach ->
  204. describe 'when user is present', ->
  205. beforeEach ->
  206. @AuthenticationController.getLoggedInUserId =
  207. sinon.stub().returns(@userId.toString())
  208. describe 'when project does not exist', ->
  209. beforeEach ->
  210. @req = new MockRequest()
  211. @req.url = '/123abc'
  212. @res = new MockResponse()
  213. @res.redirect = sinon.stub()
  214. @next = sinon.stub()
  215. @req.params['read_and_write_token'] = '123abc'
  216. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  217. .callsArgWith(1, null, null, false)
  218. @TokenAccessHandler.findProjectWithHigherAccess =
  219. sinon.stub()
  220. .callsArgWith(2, null, @project)
  221. @TokenAccessController.readAndWriteToken @req, @res, @next
  222. it 'should redirect to v1', (done) ->
  223. expect(@res.redirect.callCount).to.equal 1
  224. expect(@res.redirect.calledWith(
  225. 302,
  226. '/sign_in_to_v1?return_to=/123abc'
  227. )).to.equal true
  228. done()
  229. describe 'when token access is off, but user has higher access anyway', ->
  230. beforeEach ->
  231. @req = new MockRequest()
  232. @res = new MockResponse()
  233. @res.redirect = sinon.stub()
  234. @next = sinon.stub()
  235. @req.params['read_and_write_token'] = @readAndWriteToken
  236. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  237. .callsArgWith(1, null, null, true)
  238. @TokenAccessHandler.findProjectWithHigherAccess =
  239. sinon.stub()
  240. .callsArgWith(2, null, @project)
  241. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  242. .callsArgWith(2, null)
  243. @ProjectController.loadEditor = sinon.stub()
  244. @TokenAccessController.readAndWriteToken @req, @res, @next
  245. it 'should try to find a project with this token', (done) ->
  246. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  247. .to.equal 1
  248. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken
  249. .calledWith(@readAndWriteToken)
  250. ).to.equal true
  251. done()
  252. it 'should check if user has higher access to the token project', (done) ->
  253. expect(
  254. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  255. ).to.equal 1
  256. done()
  257. it 'should not add the user to the project with read-write access', (done) ->
  258. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  259. .to.equal 0
  260. done()
  261. it 'should not pass control to loadEditor', (done) ->
  262. expect(@ProjectController.loadEditor.callCount).to.equal 0
  263. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  264. done()
  265. it 'should not call next with a not-found error', (done) ->
  266. expect(@next.callCount).to.equal 0
  267. done()
  268. it 'should redirect to the canonical project url', (done) ->
  269. expect(@res.redirect.callCount).to.equal 1
  270. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  271. done()
  272. describe 'when higher access is not available', ->
  273. beforeEach ->
  274. @req = new MockRequest()
  275. @res = new MockResponse()
  276. @next = sinon.stub()
  277. @req.params['read_and_write_token'] = @readAndWriteToken
  278. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  279. .callsArgWith(1, null, null, true)
  280. @TokenAccessHandler.findProjectWithHigherAccess =
  281. sinon.stub()
  282. .callsArgWith(2, null, null)
  283. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  284. .callsArgWith(2, null)
  285. @ProjectController.loadEditor = sinon.stub()
  286. @TokenAccessController.readAndWriteToken @req, @res, @next
  287. it 'should try to find a project with this token', (done) ->
  288. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  289. .to.equal 1
  290. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  291. @readAndWriteToken
  292. )).to.equal true
  293. done()
  294. it 'should check if user has higher access to the token project', (done) ->
  295. expect(
  296. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  297. ).to.equal 1
  298. done()
  299. it 'should not add the user to the project with read-write access', (done) ->
  300. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  301. .to.equal 0
  302. done()
  303. it 'should not pass control to loadEditor', (done) ->
  304. expect(@ProjectController.loadEditor.callCount).to.equal 0
  305. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  306. done()
  307. it 'should call next with a not-found error', (done) ->
  308. expect(@next.callCount).to.equal 1
  309. expect(@next.lastCall.args[0]).to.be.instanceof Error
  310. done()
  311. describe 'when adding user to project produces an error', ->
  312. beforeEach ->
  313. @req = new MockRequest()
  314. @res = new MockResponse()
  315. @next = sinon.stub()
  316. @req.params['read_and_write_token'] = @readAndWriteToken
  317. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  318. .callsArgWith(1, null, @project, true)
  319. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  320. .callsArgWith(2, new Error('woops'))
  321. @ProjectController.loadEditor = sinon.stub()
  322. @TokenAccessController.readAndWriteToken @req, @res, @next
  323. it 'should try to find a project with this token', (done) ->
  324. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  325. .to.equal 1
  326. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  327. .to.equal true
  328. done()
  329. it 'should add the user to the project with read-write access', (done) ->
  330. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  331. .to.equal 1
  332. expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  333. @userId.toString(), @projectId
  334. ))
  335. .to.equal true
  336. done()
  337. it 'should not pass control to loadEditor', (done) ->
  338. expect(@ProjectController.loadEditor.callCount).to.equal 0
  339. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  340. done()
  341. it 'should call next with an error', (done) ->
  342. expect(@next.callCount).to.equal 1
  343. expect(@next.lastCall.args[0]).to.be.instanceof Error
  344. done()
  345. describe 'readOnlyToken', ->
  346. beforeEach ->
  347. @TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, true)
  348. describe 'when access not allowed by v1 api', ->
  349. beforeEach ->
  350. @req = new MockRequest()
  351. @res = new MockResponse()
  352. @res.redirect = sinon.stub()
  353. @next = sinon.stub()
  354. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  355. .callsArgWith(1, null, @project, true)
  356. @TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, false, 'doc-url')
  357. @TokenAccessController.readOnlyToken @req, @res, @next
  358. it 'should redirect to doc-url', ->
  359. expect(@res.redirect.calledWith('doc-url')).to.equal true
  360. describe 'with a user', ->
  361. beforeEach ->
  362. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  363. describe 'when all goes well', ->
  364. beforeEach ->
  365. @req = new MockRequest()
  366. @res = new MockResponse()
  367. @next = sinon.stub()
  368. @req.params['read_only_token'] = @readOnlyToken
  369. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  370. .callsArgWith(1, null, @project, true)
  371. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  372. .callsArgWith(2, null)
  373. @ProjectController.loadEditor = sinon.stub()
  374. @TokenAccessController.readOnlyToken @req, @res, @next
  375. it 'should try to find a project with this token', (done) ->
  376. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  377. .to.equal 1
  378. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  379. .to.equal true
  380. done()
  381. it 'should add the user to the project with read-only access', (done) ->
  382. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  383. .to.equal 1
  384. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  385. @userId.toString(), @projectId
  386. ))
  387. .to.equal true
  388. done()
  389. it 'should pass control to loadEditor', (done) ->
  390. expect(@req.params.Project_id).to.equal @projectId.toString()
  391. expect(@ProjectController.loadEditor.callCount).to.equal 1
  392. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  393. done()
  394. describe 'when the user is already the owner', ->
  395. beforeEach ->
  396. @req = new MockRequest()
  397. @res = new MockResponse()
  398. @next = sinon.stub()
  399. @req.params['read_only_token'] = @readOnlyToken
  400. @project.owner_ref = @userId
  401. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  402. .callsArgWith(1, null, @project, true)
  403. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  404. .callsArgWith(2, null)
  405. @ProjectController.loadEditor = sinon.stub()
  406. @TokenAccessController.readOnlyToken @req, @res, @next
  407. it 'should try to find a project with this token', (done) ->
  408. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  409. .to.equal 1
  410. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  411. .to.equal true
  412. done()
  413. it 'should not add the user to the project with read-only access', (done) ->
  414. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  415. .to.equal 0
  416. done()
  417. it 'should pass control to loadEditor', (done) ->
  418. expect(@req.params.Project_id).to.equal @projectId.toString()
  419. expect(@ProjectController.loadEditor.callCount).to.equal 1
  420. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  421. done()
  422. describe 'when findProject produces an error', ->
  423. beforeEach ->
  424. @req = new MockRequest()
  425. @res = new MockResponse()
  426. @next = sinon.stub()
  427. @req.params['read_only_token'] = @readOnlyToken
  428. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  429. .callsArgWith(1, new Error('woops'))
  430. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  431. .callsArgWith(2, null)
  432. @ProjectController.loadEditor = sinon.stub()
  433. @TokenAccessController.readOnlyToken @req, @res, @next
  434. it 'should try to find a project with this token', (done) ->
  435. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  436. .to.equal 1
  437. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  438. .to.equal true
  439. done()
  440. it 'should not add the user to the project with read-only access', (done) ->
  441. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  442. .to.equal 0
  443. done()
  444. it 'should not pass control to loadEditor', (done) ->
  445. expect(@ProjectController.loadEditor.callCount).to.equal 0
  446. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  447. done()
  448. it 'should call next with an error', (done) ->
  449. expect(@next.callCount).to.equal 1
  450. expect(@next.lastCall.args[0]).to.be.instanceof Error
  451. done()
  452. describe 'when findProject does not find a project', ->
  453. describe 'when token access is off, but user has higher access anyway', ->
  454. beforeEach ->
  455. @req = new MockRequest()
  456. @res = new MockResponse()
  457. @res.redirect = sinon.stub()
  458. @next = sinon.stub()
  459. @req.params['read_and_write_token'] = @readAndWriteToken
  460. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  461. .callsArgWith(1, null, null, true)
  462. @TokenAccessHandler.findProjectWithHigherAccess =
  463. sinon.stub()
  464. .callsArgWith(2, null, @project)
  465. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  466. .callsArgWith(2, null)
  467. @ProjectController.loadEditor = sinon.stub()
  468. @TokenAccessController.readAndWriteToken @req, @res, @next
  469. it 'should try to find a project with this token', (done) ->
  470. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  471. .to.equal 1
  472. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  473. .to.equal true
  474. done()
  475. it 'should check if user has higher access to the token project', (done) ->
  476. expect(
  477. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  478. ).to.equal 1
  479. done()
  480. it 'should not add the user to the project with read-write access', (done) ->
  481. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  482. .to.equal 0
  483. done()
  484. it 'should not pass control to loadEditor', (done) ->
  485. expect(@ProjectController.loadEditor.callCount).to.equal 0
  486. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  487. done()
  488. it 'should not call next with a not-found error', (done) ->
  489. expect(@next.callCount).to.equal 0
  490. done()
  491. it 'should redirect to the canonical project url', (done) ->
  492. expect(@res.redirect.callCount).to.equal 1
  493. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  494. done()
  495. describe 'when higher access is not available', ->
  496. beforeEach ->
  497. @req = new MockRequest()
  498. @res = new MockResponse()
  499. @next = sinon.stub()
  500. @req.params['read_and_write_token'] = @readAndWriteToken
  501. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  502. .callsArgWith(1, null, null, true)
  503. @TokenAccessHandler.findProjectWithHigherAccess =
  504. sinon.stub()
  505. .callsArgWith(2, null, null)
  506. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  507. .callsArgWith(2, null)
  508. @ProjectController.loadEditor = sinon.stub()
  509. @TokenAccessController.readAndWriteToken @req, @res, @next
  510. it 'should try to find a project with this token', (done) ->
  511. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  512. .to.equal 1
  513. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  514. @readAndWriteToken
  515. )).to.equal true
  516. done()
  517. it 'should check if user has higher access to the token project', (done) ->
  518. expect(
  519. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  520. ).to.equal 1
  521. done()
  522. it 'should not add the user to the project with read-write access', (done) ->
  523. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  524. .to.equal 0
  525. done()
  526. it 'should not pass control to loadEditor', (done) ->
  527. expect(@ProjectController.loadEditor.callCount).to.equal 0
  528. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  529. done()
  530. it 'should call next with a not-found error', (done) ->
  531. expect(@next.callCount).to.equal 1
  532. expect(@next.lastCall.args[0]).to.be.instanceof Error
  533. done()
  534. describe 'when adding user to project produces an error', ->
  535. beforeEach ->
  536. @req = new MockRequest()
  537. @res = new MockResponse()
  538. @next = sinon.stub()
  539. @req.params['read_only_token'] = @readOnlyToken
  540. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  541. .callsArgWith(1, null, @project, true)
  542. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  543. .callsArgWith(2, new Error('woops'))
  544. @ProjectController.loadEditor = sinon.stub()
  545. @TokenAccessController.readOnlyToken @req, @res, @next
  546. it 'should try to find a project with this token', (done) ->
  547. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  548. .to.equal 1
  549. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  550. .to.equal true
  551. done()
  552. it 'should add the user to the project with read-only access', (done) ->
  553. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  554. .to.equal 1
  555. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  556. @userId.toString(), @projectId
  557. ))
  558. .to.equal true
  559. done()
  560. it 'should not pass control to loadEditor', (done) ->
  561. expect(@ProjectController.loadEditor.callCount).to.equal 0
  562. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  563. done()
  564. it 'should call next with an error', (done) ->
  565. expect(@next.callCount).to.equal 1
  566. expect(@next.lastCall.args[0]).to.be.instanceof Error
  567. done()
  568. describe 'anonymous', ->
  569. beforeEach ->
  570. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
  571. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  572. describe 'when all goes well', ->
  573. beforeEach ->
  574. @req = new MockRequest()
  575. @res = new MockResponse()
  576. @next = sinon.stub()
  577. @req.params['read_only_token'] = @readOnlyToken
  578. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  579. .callsArgWith(1, null, @project, true)
  580. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  581. .callsArgWith(2, null)
  582. @ProjectController.loadEditor = sinon.stub()
  583. @TokenAccessController.readOnlyToken @req, @res, @next
  584. it 'should try to find a project with this token', (done) ->
  585. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  586. .to.equal 1
  587. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  588. .to.equal true
  589. done()
  590. it 'should give the user session read-only access', (done) ->
  591. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  592. .to.equal 1
  593. expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
  594. @req, @projectId, @readOnlyToken
  595. ))
  596. .to.equal true
  597. done()
  598. it 'should not add the user to the project with read-only access', (done) ->
  599. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  600. .to.equal 0
  601. done()
  602. it 'should pass control to loadEditor', (done) ->
  603. expect(@req.params.Project_id).to.equal @projectId.toString()
  604. expect(@req._anonymousAccessToken).to.equal @readOnlyToken
  605. expect(@ProjectController.loadEditor.callCount).to.equal 1
  606. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  607. done()
  608. describe 'when findProject produces an error', ->
  609. beforeEach ->
  610. @req = new MockRequest()
  611. @res = new MockResponse()
  612. @next = sinon.stub()
  613. @req.params['read_only_token'] = @readOnlyToken
  614. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  615. .callsArgWith(1, new Error('woops'))
  616. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  617. .callsArgWith(2, null)
  618. @ProjectController.loadEditor = sinon.stub()
  619. @TokenAccessController.readOnlyToken @req, @res, @next
  620. it 'should try to find a project with this token', (done) ->
  621. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  622. .to.equal 1
  623. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  624. .to.equal true
  625. done()
  626. it 'should not give the user session read-only access', (done) ->
  627. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  628. .to.equal 0
  629. done()
  630. it 'should not add the user to the project with read-only access', (done) ->
  631. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  632. .to.equal 0
  633. done()
  634. it 'should not pass control to loadEditor', (done) ->
  635. expect(@ProjectController.loadEditor.callCount).to.equal 0
  636. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  637. done()
  638. it 'should call next with an error', (done) ->
  639. expect(@next.callCount).to.equal 1
  640. expect(@next.lastCall.args[0]).to.be.instanceof Error
  641. done()
  642. describe 'when findProject does not find a project', ->
  643. beforeEach ->
  644. @req = new MockRequest()
  645. @res = new MockResponse()
  646. @res.redirect = sinon.stub()
  647. @next = sinon.stub()
  648. @req.params['read_only_token'] = @readOnlyToken
  649. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  650. .callsArgWith(1, null, null)
  651. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  652. .callsArgWith(2, null)
  653. @ProjectController.loadEditor = sinon.stub()
  654. @TokenAccessController.readOnlyToken @req, @res, @next
  655. it 'should try to find a project with this token', (done) ->
  656. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  657. .to.equal 1
  658. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  659. .to.equal true
  660. done()
  661. it 'should not give the user session read-only access', (done) ->
  662. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  663. .to.equal 0
  664. done()
  665. it 'should not pass control to loadEditor', (done) ->
  666. expect(@ProjectController.loadEditor.callCount).to.equal 0
  667. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  668. done()
  669. it 'should not add the user to the project with read-only access', (done) ->
  670. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  671. .to.equal 0
  672. done()
  673. it 'should redirect to v1', (done) ->
  674. expect(@res.redirect.callCount).to.equal 1
  675. expect(@res.redirect.calledWith(
  676. 302,
  677. "http://overleaf.test:5000/read/#{@readOnlyToken}"
  678. )).to.equal true
  679. done()