TokenAccessControllerTests.coffee 28 KB

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