TokenAccessControllerTests.coffee 29 KB

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