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. @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)
  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)
  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)
  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)
  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)
  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.firstCall.args[0])
  222. .to.equal 'http://overleaf.test:5000/123abc'
  223. done()
  224. describe 'when token access is off, but user has higher access anyway', ->
  225. beforeEach ->
  226. @req = new MockRequest()
  227. @res = new MockResponse()
  228. @res.redirect = sinon.stub()
  229. @next = sinon.stub()
  230. @req.params['read_and_write_token'] = @readAndWriteToken
  231. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  232. .callsArgWith(1, null, null)
  233. @TokenAccessHandler.findProjectWithHigherAccess =
  234. sinon.stub()
  235. .callsArgWith(2, null, @project, true)
  236. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  237. .callsArgWith(2, null)
  238. @ProjectController.loadEditor = sinon.stub()
  239. @TokenAccessController.readAndWriteToken @req, @res, @next
  240. it 'should try to find a project with this token', (done) ->
  241. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  242. .to.equal 1
  243. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken
  244. .calledWith(@readAndWriteToken)
  245. ).to.equal true
  246. done()
  247. it 'should check if user has higher access to the token project', (done) ->
  248. expect(
  249. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  250. ).to.equal 1
  251. done()
  252. it 'should not add the user to the project with read-write access', (done) ->
  253. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  254. .to.equal 0
  255. done()
  256. it 'should not pass control to loadEditor', (done) ->
  257. expect(@ProjectController.loadEditor.callCount).to.equal 0
  258. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  259. done()
  260. it 'should not call next with a not-found error', (done) ->
  261. expect(@next.callCount).to.equal 0
  262. done()
  263. it 'should redirect to the canonical project url', (done) ->
  264. expect(@res.redirect.callCount).to.equal 1
  265. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  266. done()
  267. describe 'when higher access is not available', ->
  268. beforeEach ->
  269. @req = new MockRequest()
  270. @res = new MockResponse()
  271. @next = sinon.stub()
  272. @req.params['read_and_write_token'] = @readAndWriteToken
  273. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  274. .callsArgWith(1, null, null)
  275. @TokenAccessHandler.findProjectWithHigherAccess =
  276. sinon.stub()
  277. .callsArgWith(2, null, null, true)
  278. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  279. .callsArgWith(2, null)
  280. @ProjectController.loadEditor = sinon.stub()
  281. @TokenAccessController.readAndWriteToken @req, @res, @next
  282. it 'should try to find a project with this token', (done) ->
  283. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  284. .to.equal 1
  285. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  286. @readAndWriteToken
  287. )).to.equal true
  288. done()
  289. it 'should check if user has higher access to the token project', (done) ->
  290. expect(
  291. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  292. ).to.equal 1
  293. done()
  294. it 'should not add the user to the project with read-write access', (done) ->
  295. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  296. .to.equal 0
  297. done()
  298. it 'should not pass control to loadEditor', (done) ->
  299. expect(@ProjectController.loadEditor.callCount).to.equal 0
  300. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  301. done()
  302. it 'should call next with a not-found error', (done) ->
  303. expect(@next.callCount).to.equal 1
  304. expect(@next.lastCall.args[0]).to.be.instanceof Error
  305. done()
  306. describe 'when adding user to project produces an error', ->
  307. beforeEach ->
  308. @req = new MockRequest()
  309. @res = new MockResponse()
  310. @next = sinon.stub()
  311. @req.params['read_and_write_token'] = @readAndWriteToken
  312. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  313. .callsArgWith(1, null, @project)
  314. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  315. .callsArgWith(2, new Error('woops'))
  316. @ProjectController.loadEditor = sinon.stub()
  317. @TokenAccessController.readAndWriteToken @req, @res, @next
  318. it 'should try to find a project with this token', (done) ->
  319. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  320. .to.equal 1
  321. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  322. .to.equal true
  323. done()
  324. it 'should add the user to the project with read-write access', (done) ->
  325. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  326. .to.equal 1
  327. expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  328. @userId.toString(), @projectId
  329. ))
  330. .to.equal true
  331. done()
  332. it 'should not pass control to loadEditor', (done) ->
  333. expect(@ProjectController.loadEditor.callCount).to.equal 0
  334. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  335. done()
  336. it 'should call next with an error', (done) ->
  337. expect(@next.callCount).to.equal 1
  338. expect(@next.lastCall.args[0]).to.be.instanceof Error
  339. done()
  340. describe 'readOnlyToken', ->
  341. beforeEach ->
  342. describe 'with a user', ->
  343. beforeEach ->
  344. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  345. describe 'when all goes well', ->
  346. beforeEach ->
  347. @req = new MockRequest()
  348. @res = new MockResponse()
  349. @next = sinon.stub()
  350. @req.params['read_only_token'] = @readOnlyToken
  351. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  352. .callsArgWith(1, null, @project)
  353. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  354. .callsArgWith(2, null)
  355. @ProjectController.loadEditor = sinon.stub()
  356. @TokenAccessController.readOnlyToken @req, @res, @next
  357. it 'should try to find a project with this token', (done) ->
  358. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  359. .to.equal 1
  360. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  361. .to.equal true
  362. done()
  363. it 'should add the user to the project with read-only access', (done) ->
  364. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  365. .to.equal 1
  366. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  367. @userId.toString(), @projectId
  368. ))
  369. .to.equal true
  370. done()
  371. it 'should pass control to loadEditor', (done) ->
  372. expect(@req.params.Project_id).to.equal @projectId.toString()
  373. expect(@ProjectController.loadEditor.callCount).to.equal 1
  374. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  375. done()
  376. describe 'when the user is already the owner', ->
  377. beforeEach ->
  378. @req = new MockRequest()
  379. @res = new MockResponse()
  380. @next = sinon.stub()
  381. @req.params['read_only_token'] = @readOnlyToken
  382. @project.owner_ref = @userId
  383. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  384. .callsArgWith(1, null, @project)
  385. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  386. .callsArgWith(2, null)
  387. @ProjectController.loadEditor = sinon.stub()
  388. @TokenAccessController.readOnlyToken @req, @res, @next
  389. it 'should try to find a project with this token', (done) ->
  390. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  391. .to.equal 1
  392. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  393. .to.equal true
  394. done()
  395. it 'should not add the user to the project with read-only access', (done) ->
  396. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  397. .to.equal 0
  398. done()
  399. it 'should pass control to loadEditor', (done) ->
  400. expect(@req.params.Project_id).to.equal @projectId.toString()
  401. expect(@ProjectController.loadEditor.callCount).to.equal 1
  402. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  403. done()
  404. describe 'when findProject produces an error', ->
  405. beforeEach ->
  406. @req = new MockRequest()
  407. @res = new MockResponse()
  408. @next = sinon.stub()
  409. @req.params['read_only_token'] = @readOnlyToken
  410. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  411. .callsArgWith(1, new Error('woops'))
  412. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  413. .callsArgWith(2, null)
  414. @ProjectController.loadEditor = sinon.stub()
  415. @TokenAccessController.readOnlyToken @req, @res, @next
  416. it 'should try to find a project with this token', (done) ->
  417. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  418. .to.equal 1
  419. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  420. .to.equal true
  421. done()
  422. it 'should not add the user to the project with read-only access', (done) ->
  423. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  424. .to.equal 0
  425. done()
  426. it 'should not pass control to loadEditor', (done) ->
  427. expect(@ProjectController.loadEditor.callCount).to.equal 0
  428. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  429. done()
  430. it 'should call next with an error', (done) ->
  431. expect(@next.callCount).to.equal 1
  432. expect(@next.lastCall.args[0]).to.be.instanceof Error
  433. done()
  434. ##
  435. describe 'when findProject does not find a project', ->
  436. beforeEach ->
  437. describe 'when project does not exist', ->
  438. beforeEach ->
  439. @req = new MockRequest()
  440. @req.url = '/123abc'
  441. @res = new MockResponse()
  442. @res.redirect = sinon.stub()
  443. @next = sinon.stub()
  444. @req.params['read_and_write_token'] = '123abc'
  445. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  446. .callsArgWith(1, null, null)
  447. @TokenAccessHandler.findProjectWithHigherAccess =
  448. sinon.stub()
  449. .callsArgWith(2, null, @project, false)
  450. @TokenAccessController.readOnlyToken @req, @res, @next
  451. it 'should return a ProjectNotTokenAccessError', (done) ->
  452. expect(@res.redirect.callCount).to.equal 1
  453. expect(@res.redirect.firstCall.args[0])
  454. .to.equal 'http://overleaf.test:5000/123abc'
  455. done()
  456. describe 'when token access is off, but user has higher access anyway', ->
  457. beforeEach ->
  458. @req = new MockRequest()
  459. @res = new MockResponse()
  460. @res.redirect = sinon.stub()
  461. @next = sinon.stub()
  462. @req.params['read_and_write_token'] = @readAndWriteToken
  463. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  464. .callsArgWith(1, null, null)
  465. @TokenAccessHandler.findProjectWithHigherAccess =
  466. sinon.stub()
  467. .callsArgWith(2, null, @project, true)
  468. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  469. .callsArgWith(2, null)
  470. @ProjectController.loadEditor = sinon.stub()
  471. @TokenAccessController.readAndWriteToken @req, @res, @next
  472. it 'should try to find a project with this token', (done) ->
  473. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  474. .to.equal 1
  475. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  476. .to.equal true
  477. done()
  478. it 'should check if user has higher access to the token project', (done) ->
  479. expect(
  480. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  481. ).to.equal 1
  482. done()
  483. it 'should not add the user to the project with read-write access', (done) ->
  484. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  485. .to.equal 0
  486. done()
  487. it 'should not pass control to loadEditor', (done) ->
  488. expect(@ProjectController.loadEditor.callCount).to.equal 0
  489. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  490. done()
  491. it 'should not call next with a not-found error', (done) ->
  492. expect(@next.callCount).to.equal 0
  493. done()
  494. it 'should redirect to the canonical project url', (done) ->
  495. expect(@res.redirect.callCount).to.equal 1
  496. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  497. done()
  498. describe 'when higher access is not available', ->
  499. beforeEach ->
  500. @req = new MockRequest()
  501. @res = new MockResponse()
  502. @next = sinon.stub()
  503. @req.params['read_and_write_token'] = @readAndWriteToken
  504. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  505. .callsArgWith(1, null, null)
  506. @TokenAccessHandler.findProjectWithHigherAccess =
  507. sinon.stub()
  508. .callsArgWith(2, null, null, true)
  509. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  510. .callsArgWith(2, null)
  511. @ProjectController.loadEditor = sinon.stub()
  512. @TokenAccessController.readAndWriteToken @req, @res, @next
  513. it 'should try to find a project with this token', (done) ->
  514. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  515. .to.equal 1
  516. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  517. @readAndWriteToken
  518. )).to.equal true
  519. done()
  520. it 'should check if user has higher access to the token project', (done) ->
  521. expect(
  522. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  523. ).to.equal 1
  524. done()
  525. it 'should not add the user to the project with read-write access', (done) ->
  526. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  527. .to.equal 0
  528. done()
  529. it 'should not pass control to loadEditor', (done) ->
  530. expect(@ProjectController.loadEditor.callCount).to.equal 0
  531. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  532. done()
  533. it 'should call next with a not-found error', (done) ->
  534. expect(@next.callCount).to.equal 1
  535. expect(@next.lastCall.args[0]).to.be.instanceof Error
  536. done()
  537. describe 'when adding user to project produces an error', ->
  538. beforeEach ->
  539. @req = new MockRequest()
  540. @res = new MockResponse()
  541. @next = sinon.stub()
  542. @req.params['read_only_token'] = @readOnlyToken
  543. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  544. .callsArgWith(1, null, @project)
  545. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  546. .callsArgWith(2, new Error('woops'))
  547. @ProjectController.loadEditor = sinon.stub()
  548. @TokenAccessController.readOnlyToken @req, @res, @next
  549. it 'should try to find a project with this token', (done) ->
  550. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  551. .to.equal 1
  552. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  553. .to.equal true
  554. done()
  555. it 'should add the user to the project with read-only access', (done) ->
  556. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  557. .to.equal 1
  558. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  559. @userId.toString(), @projectId
  560. ))
  561. .to.equal true
  562. done()
  563. it 'should not pass control to loadEditor', (done) ->
  564. expect(@ProjectController.loadEditor.callCount).to.equal 0
  565. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  566. done()
  567. it 'should call next with an error', (done) ->
  568. expect(@next.callCount).to.equal 1
  569. expect(@next.lastCall.args[0]).to.be.instanceof Error
  570. done()
  571. describe 'anonymous', ->
  572. beforeEach ->
  573. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
  574. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  575. describe 'when all goes well', ->
  576. beforeEach ->
  577. @req = new MockRequest()
  578. @res = new MockResponse()
  579. @next = sinon.stub()
  580. @req.params['read_only_token'] = @readOnlyToken
  581. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  582. .callsArgWith(1, null, @project)
  583. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  584. .callsArgWith(2, null)
  585. @ProjectController.loadEditor = sinon.stub()
  586. @TokenAccessController.readOnlyToken @req, @res, @next
  587. it 'should try to find a project with this token', (done) ->
  588. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  589. .to.equal 1
  590. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  591. .to.equal true
  592. done()
  593. it 'should give the user session read-only access', (done) ->
  594. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  595. .to.equal 1
  596. expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
  597. @req, @projectId, @readOnlyToken
  598. ))
  599. .to.equal true
  600. done()
  601. it 'should not add the user to the project with read-only access', (done) ->
  602. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  603. .to.equal 0
  604. done()
  605. it 'should pass control to loadEditor', (done) ->
  606. expect(@req.params.Project_id).to.equal @projectId.toString()
  607. expect(@req._anonymousAccessToken).to.equal @readOnlyToken
  608. expect(@ProjectController.loadEditor.callCount).to.equal 1
  609. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  610. done()
  611. describe 'when findProject produces an error', ->
  612. beforeEach ->
  613. @req = new MockRequest()
  614. @res = new MockResponse()
  615. @next = sinon.stub()
  616. @req.params['read_only_token'] = @readOnlyToken
  617. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  618. .callsArgWith(1, new Error('woops'))
  619. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  620. .callsArgWith(2, null)
  621. @ProjectController.loadEditor = sinon.stub()
  622. @TokenAccessController.readOnlyToken @req, @res, @next
  623. it 'should try to find a project with this token', (done) ->
  624. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  625. .to.equal 1
  626. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  627. .to.equal true
  628. done()
  629. it 'should not give the user session read-only access', (done) ->
  630. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  631. .to.equal 0
  632. done()
  633. it 'should not add the user to the project with read-only access', (done) ->
  634. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  635. .to.equal 0
  636. done()
  637. it 'should not pass control to loadEditor', (done) ->
  638. expect(@ProjectController.loadEditor.callCount).to.equal 0
  639. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  640. done()
  641. it 'should call next with an error', (done) ->
  642. expect(@next.callCount).to.equal 1
  643. expect(@next.lastCall.args[0]).to.be.instanceof Error
  644. done()
  645. describe 'when findProject does not find a project', ->
  646. beforeEach ->
  647. @req = new MockRequest()
  648. @res = new MockResponse()
  649. @next = sinon.stub()
  650. @req.params['read_only_token'] = @readOnlyToken
  651. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  652. .callsArgWith(1, null, null)
  653. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  654. .callsArgWith(2, null)
  655. @ProjectController.loadEditor = sinon.stub()
  656. @TokenAccessController.readOnlyToken @req, @res, @next
  657. it 'should try to find a project with this token', (done) ->
  658. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  659. .to.equal 1
  660. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  661. .to.equal true
  662. done()
  663. it 'should not give the user session read-only access', (done) ->
  664. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  665. .to.equal 0
  666. done()
  667. it 'should not pass control to loadEditor', (done) ->
  668. expect(@ProjectController.loadEditor.callCount).to.equal 0
  669. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  670. done()
  671. it 'should not add the user to the project with read-only access', (done) ->
  672. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  673. .to.equal 0
  674. done()
  675. it 'should call next with a not-found error', (done) ->
  676. expect(@next.callCount).to.equal 1
  677. expect(@next.lastCall.args[0]).to.be.instanceof Error
  678. done()