TokenAccessControllerTests.coffee 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221
  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. getV1DocPublishedInfo: sinon.stub().yields(null, {
  31. allow: true
  32. })
  33. getV1DocInfo: sinon.stub().yields(null, {
  34. exists: true
  35. exported: false
  36. })
  37. }
  38. '../../infrastructure/Features': @Features = {
  39. hasFeature: sinon.stub().returns(false)
  40. }
  41. 'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
  42. 'settings-sharelatex': {
  43. overleaf:
  44. host: 'http://overleaf.test:5000'
  45. }
  46. '../V1/V1Api': @V1Api = {
  47. request: sinon.stub().callsArgWith(1, null, {}, { allow: true })
  48. }
  49. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  50. describe 'readAndWriteToken', ->
  51. beforeEach ->
  52. describe 'when all goes well', ->
  53. beforeEach ->
  54. @req = new MockRequest()
  55. @res = new MockResponse()
  56. @next = sinon.stub()
  57. @req.params['read_and_write_token'] = @readAndWriteToken
  58. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  59. .callsArgWith(1, null, @project, true)
  60. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  61. .callsArgWith(2, null)
  62. @ProjectController.loadEditor = sinon.stub()
  63. @AuthenticationController.setRedirectInSession = sinon.stub()
  64. @TokenAccessController.readAndWriteToken @req, @res, @next
  65. it 'should try to find a project with this token', (done) ->
  66. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  67. .to.equal 1
  68. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  69. .to.equal true
  70. done()
  71. it 'should add the user to the project with read-write access', (done) ->
  72. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  73. .to.equal 1
  74. expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  75. @userId.toString(), @projectId
  76. ))
  77. .to.equal true
  78. done()
  79. it 'should pass control to loadEditor', (done) ->
  80. expect(@req.params.Project_id).to.equal @projectId.toString()
  81. expect(@ProjectController.loadEditor.callCount).to.equal 1
  82. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  83. done()
  84. describe 'when the user is already the owner', ->
  85. beforeEach ->
  86. @req = new MockRequest()
  87. @res = new MockResponse()
  88. @next = sinon.stub()
  89. @req.params['read_and_write_token'] = @readAndWriteToken
  90. @project.owner_ref = @userId
  91. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  92. .callsArgWith(1, null, @project, true)
  93. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  94. .callsArgWith(2, null)
  95. @ProjectController.loadEditor = sinon.stub()
  96. @TokenAccessController.readAndWriteToken @req, @res, @next
  97. it 'should try to find a project with this token', (done) ->
  98. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  99. .to.equal 1
  100. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  101. .to.equal true
  102. done()
  103. it 'should not add the user to the project with read-write access', (done) ->
  104. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  105. .to.equal 0
  106. done()
  107. it 'should pass control to loadEditor', (done) ->
  108. expect(@req.params.Project_id).to.equal @projectId.toString()
  109. expect(@ProjectController.loadEditor.callCount).to.equal 1
  110. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  111. done()
  112. describe 'when there is no user', ->
  113. beforeEach ->
  114. @AuthenticationController.getLoggedInUserId =
  115. sinon.stub().returns(null)
  116. describe 'when anonymous read-write access is enabled', ->
  117. beforeEach ->
  118. @TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = true
  119. @req = new MockRequest()
  120. @res = new MockResponse()
  121. @next = sinon.stub()
  122. @req.params['read_and_write_token'] = @readAndWriteToken
  123. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  124. .callsArgWith(1, null, @project, true)
  125. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  126. .callsArgWith(2, null)
  127. @ProjectController.loadEditor = sinon.stub()
  128. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  129. @TokenAccessController.readAndWriteToken @req, @res, @next
  130. it 'should not add the user to the project with read-write access', (done) ->
  131. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  132. .to.equal 0
  133. done()
  134. it 'should give the user session token access', (done) ->
  135. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  136. .to.equal 1
  137. expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
  138. @req, @projectId, @readAndWriteToken
  139. ))
  140. .to.equal true
  141. done()
  142. it 'should pass control to loadEditor', (done) ->
  143. expect(@req.params.Project_id).to.equal @projectId.toString()
  144. expect(@ProjectController.loadEditor.callCount).to.equal 1
  145. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  146. done()
  147. describe 'when anonymous read-write access is not enabled', ->
  148. beforeEach ->
  149. @TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = false
  150. @req = new MockRequest()
  151. @res = new MockResponse()
  152. @res.redirect = sinon.stub()
  153. @next = sinon.stub()
  154. @req.params['read_and_write_token'] = @readAndWriteToken
  155. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  156. .callsArgWith(1, null, @project, true)
  157. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  158. .callsArgWith(2, null)
  159. @ProjectController.loadEditor = sinon.stub()
  160. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  161. @AuthenticationController.setRedirectInSession = sinon.stub()
  162. @TokenAccessController.readAndWriteToken @req, @res, @next
  163. it 'should not add the user to the project with read-write access', (done) ->
  164. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  165. .to.equal 0
  166. done()
  167. it 'should give the user session token access', (done) ->
  168. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  169. .to.equal 0
  170. done()
  171. it 'should not pass control to loadEditor', (done) ->
  172. expect(@ProjectController.loadEditor.callCount).to.equal 0
  173. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  174. done()
  175. it 'should set redirect in session', (done) ->
  176. expect(@AuthenticationController.setRedirectInSession.callCount).to.equal 1
  177. expect(@AuthenticationController.setRedirectInSession.calledWith(@req)).to.equal true
  178. done()
  179. it 'should redirect to restricted page', (done) ->
  180. expect(@res.redirect.callCount).to.equal 1
  181. expect(@res.redirect.calledWith('/restricted')).to.equal true
  182. done()
  183. describe 'when findProject produces an error', ->
  184. beforeEach ->
  185. @req = new MockRequest()
  186. @res = new MockResponse()
  187. @next = sinon.stub()
  188. @req.params['read_and_write_token'] = @readAndWriteToken
  189. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  190. .callsArgWith(1, new Error('woops'))
  191. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  192. .callsArgWith(2, null)
  193. @ProjectController.loadEditor = sinon.stub()
  194. @TokenAccessController.readAndWriteToken @req, @res, @next
  195. it 'should try to find a project with this token', (done) ->
  196. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  197. .to.equal 1
  198. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  199. .to.equal true
  200. done()
  201. it 'should not add the user to the project with read-write access', (done) ->
  202. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  203. .to.equal 0
  204. done()
  205. it 'should not pass control to loadEditor', (done) ->
  206. expect(@ProjectController.loadEditor.callCount).to.equal 0
  207. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  208. done()
  209. it 'should call next with an error', (done) ->
  210. expect(@next.callCount).to.equal 1
  211. expect(@next.lastCall.args[0]).to.be.instanceof Error
  212. done()
  213. describe 'when findProject does not find a project', ->
  214. beforeEach ->
  215. describe 'when user is present', ->
  216. beforeEach ->
  217. @AuthenticationController.getLoggedInUserId =
  218. sinon.stub().returns(@userId.toString())
  219. describe 'when project does not exist', ->
  220. beforeEach ->
  221. @req = new MockRequest()
  222. @req.url = '/123abc'
  223. @res = new MockResponse()
  224. @res.redirect = sinon.stub()
  225. @res.render = sinon.stub()
  226. @next = sinon.stub()
  227. @req.params['read_and_write_token'] = '123abc'
  228. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  229. .callsArgWith(1, null, null, false)
  230. describe 'when project was not exported from v1', ->
  231. beforeEach ->
  232. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  233. exists: true
  234. exported: false
  235. })
  236. @TokenAccessController.readAndWriteToken @req, @res, @next
  237. it 'should redirect to v1', (done) ->
  238. expect(@res.redirect.callCount).to.equal 1
  239. expect(@res.redirect.calledWith(
  240. 302,
  241. '/sign_in_to_v1?return_to=/123abc'
  242. )).to.equal true
  243. done()
  244. describe 'when project was not exported from v1 but forcing import to v2', ->
  245. beforeEach ->
  246. @Features.hasFeature.returns(true)
  247. describe 'with project name', ->
  248. beforeEach ->
  249. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  250. exists: true
  251. exported: false
  252. has_owner: true
  253. name: 'A title'
  254. has_assignment: false
  255. brand_info: null
  256. })
  257. @TokenAccessController.readAndWriteToken @req, @res, @next
  258. it 'should render v2-import page with name', (done) ->
  259. expect(@res.render.calledWith(
  260. 'project/v2-import',
  261. {
  262. projectId: '123abc'
  263. name: 'A title'
  264. hasOwner: true
  265. hasAssignment: false
  266. brandInfo: null
  267. }
  268. )).to.equal true
  269. done()
  270. describe 'with project owner', ->
  271. beforeEach ->
  272. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  273. exists: true
  274. exported: false
  275. has_owner: true
  276. name: 'A title'
  277. has_assignment: false
  278. brand_info: null
  279. })
  280. @TokenAccessController.readAndWriteToken @req, @res, @next
  281. it 'should render v2-import page', (done) ->
  282. expect(@res.render.calledWith(
  283. 'project/v2-import',
  284. {
  285. projectId: '123abc'
  286. hasOwner: true
  287. name: 'A title'
  288. hasAssignment: false
  289. brandInfo: null
  290. }
  291. )).to.equal true
  292. done()
  293. describe 'without project owner', ->
  294. beforeEach ->
  295. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  296. exists: true
  297. exported: false
  298. has_owner: false
  299. name: 'A title'
  300. has_assignment: false
  301. brand_info: null
  302. })
  303. @TokenAccessController.readAndWriteToken @req, @res, @next
  304. it 'should render v2-import page', (done) ->
  305. expect(@res.render.calledWith(
  306. 'project/v2-import',
  307. {
  308. projectId: '123abc',
  309. hasOwner: false
  310. name: 'A title',
  311. hasAssignment: false,
  312. brandInfo: null
  313. }
  314. )).to.equal true
  315. done()
  316. describe 'with assignment', ->
  317. beforeEach ->
  318. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  319. exists: true
  320. exported: false
  321. has_owner: false
  322. name: 'A title'
  323. has_assignment: true
  324. brand_info: null
  325. })
  326. @TokenAccessController.readAndWriteToken @req, @res, @next
  327. it 'should render v2-import page', (done) ->
  328. expect(@res.render.calledWith(
  329. 'project/v2-import',
  330. {
  331. projectId: '123abc',
  332. hasOwner: false
  333. name: 'A title',
  334. hasAssignment: true,
  335. brandInfo: null
  336. }
  337. )).to.equal true
  338. done()
  339. describe 'with brand info', ->
  340. beforeEach ->
  341. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  342. exists: true
  343. exported: false
  344. has_owner: false
  345. name: 'A title'
  346. has_assignment: false
  347. brand_info: 'wellcome'
  348. })
  349. @TokenAccessController.readAndWriteToken @req, @res, @next
  350. it 'should render v2-import page', (done) ->
  351. expect(@res.render.calledWith(
  352. 'project/v2-import',
  353. {
  354. projectId: '123abc',
  355. hasOwner: false
  356. name: 'A title',
  357. hasAssignment: false,
  358. brandInfo: 'wellcome'
  359. }
  360. )).to.equal true
  361. done()
  362. describe 'with anonymous user', ->
  363. beforeEach ->
  364. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
  365. @TokenAccessController.readAndWriteToken @req, @res, @next
  366. it 'should render anonymous import status page', (done) ->
  367. expect(@res.render.callCount).to.equal 1
  368. expect(@res.render.calledWith(
  369. 'project/v2-import',
  370. { loginRedirect: '/123abc' }
  371. )).to.equal true
  372. done()
  373. describe 'when project was exported from v1', ->
  374. beforeEach ->
  375. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  376. exists: true
  377. exported: true
  378. })
  379. @TokenAccessController.readAndWriteToken @req, @res, @next
  380. it 'should call next with a not-found error', (done) ->
  381. expect(@next.callCount).to.equal 1
  382. done()
  383. describe 'when project does not exist on v1', ->
  384. beforeEach ->
  385. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  386. exists: false
  387. exported: false
  388. })
  389. @TokenAccessController.readAndWriteToken @req, @res, @next
  390. it 'should call next with a not-found error', (done) ->
  391. expect(@next.callCount).to.equal 1
  392. expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
  393. done()
  394. describe 'when token access is off, but user has higher access anyway', ->
  395. beforeEach ->
  396. @req = new MockRequest()
  397. @res = new MockResponse()
  398. @res.redirect = sinon.stub()
  399. @next = sinon.stub()
  400. @req.params['read_and_write_token'] = @readAndWriteToken
  401. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  402. .callsArgWith(1, null, null, true)
  403. @TokenAccessHandler.findProjectWithHigherAccess =
  404. sinon.stub()
  405. .callsArgWith(2, null, @project)
  406. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  407. .callsArgWith(2, null)
  408. @ProjectController.loadEditor = sinon.stub()
  409. @TokenAccessController.readAndWriteToken @req, @res, @next
  410. it 'should try to find a project with this token', (done) ->
  411. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  412. .to.equal 1
  413. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken
  414. .calledWith(@readAndWriteToken)
  415. ).to.equal true
  416. done()
  417. it 'should check if user has higher access to the token project', (done) ->
  418. expect(
  419. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  420. ).to.equal 1
  421. done()
  422. it 'should not add the user to the project with read-write access', (done) ->
  423. expect(@TokenAccessHandler.addReadAndWriteUserToProject.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 not call next with a not-found error', (done) ->
  431. expect(@next.callCount).to.equal 0
  432. done()
  433. it 'should redirect to the canonical project url', (done) ->
  434. expect(@res.redirect.callCount).to.equal 1
  435. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  436. done()
  437. describe 'when higher access is not available', ->
  438. beforeEach ->
  439. @req = new MockRequest()
  440. @res = new MockResponse()
  441. @next = sinon.stub()
  442. @req.params['read_and_write_token'] = @readAndWriteToken
  443. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  444. .callsArgWith(1, null, null, true)
  445. @TokenAccessHandler.findProjectWithHigherAccess =
  446. sinon.stub()
  447. .callsArgWith(2, null, null)
  448. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  449. .callsArgWith(2, null)
  450. @ProjectController.loadEditor = sinon.stub()
  451. @TokenAccessController.readAndWriteToken @req, @res, @next
  452. it 'should try to find a project with this token', (done) ->
  453. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  454. .to.equal 1
  455. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  456. @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 call next with a not-found error', (done) ->
  473. expect(@next.callCount).to.equal 1
  474. expect(@next.lastCall.args[0]).to.be.instanceof Error
  475. done()
  476. describe 'when adding user to project produces an error', ->
  477. beforeEach ->
  478. @req = new MockRequest()
  479. @res = new MockResponse()
  480. @next = sinon.stub()
  481. @req.params['read_and_write_token'] = @readAndWriteToken
  482. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  483. .callsArgWith(1, null, @project, true)
  484. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  485. .callsArgWith(2, new Error('woops'))
  486. @ProjectController.loadEditor = sinon.stub()
  487. @TokenAccessController.readAndWriteToken @req, @res, @next
  488. it 'should try to find a project with this token', (done) ->
  489. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  490. .to.equal 1
  491. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  492. .to.equal true
  493. done()
  494. it 'should add the user to the project with read-write access', (done) ->
  495. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  496. .to.equal 1
  497. expect(@TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  498. @userId.toString(), @projectId
  499. ))
  500. .to.equal true
  501. done()
  502. it 'should not pass control to loadEditor', (done) ->
  503. expect(@ProjectController.loadEditor.callCount).to.equal 0
  504. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  505. done()
  506. it 'should call next with an error', (done) ->
  507. expect(@next.callCount).to.equal 1
  508. expect(@next.lastCall.args[0]).to.be.instanceof Error
  509. done()
  510. describe 'readOnlyToken', ->
  511. beforeEach ->
  512. @TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, true)
  513. describe 'when access not allowed by v1 api', ->
  514. beforeEach ->
  515. @req = new MockRequest()
  516. @res = new MockResponse()
  517. @res.redirect = sinon.stub()
  518. @next = sinon.stub()
  519. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  520. .callsArgWith(1, null, @project, true)
  521. @TokenAccessHandler.getV1DocPublishedInfo = sinon.stub().yields(null, {
  522. allow: false
  523. published_path: 'doc-url'
  524. })
  525. @TokenAccessController.readOnlyToken @req, @res, @next
  526. it 'should redirect to doc-url', ->
  527. expect(@res.redirect.calledWith('doc-url')).to.equal true
  528. describe 'with a user', ->
  529. beforeEach ->
  530. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  531. describe 'when all goes well', ->
  532. beforeEach ->
  533. @req = new MockRequest()
  534. @res = new MockResponse()
  535. @next = sinon.stub()
  536. @req.params['read_only_token'] = @readOnlyToken
  537. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  538. .callsArgWith(1, null, @project, true)
  539. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  540. .callsArgWith(2, null)
  541. @ProjectController.loadEditor = sinon.stub()
  542. @TokenAccessController.readOnlyToken @req, @res, @next
  543. it 'should try to find a project with this token', (done) ->
  544. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  545. .to.equal 1
  546. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  547. .to.equal true
  548. done()
  549. it 'should add the user to the project with read-only access', (done) ->
  550. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  551. .to.equal 1
  552. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  553. @userId.toString(), @projectId
  554. ))
  555. .to.equal true
  556. done()
  557. it 'should pass control to loadEditor', (done) ->
  558. expect(@req.params.Project_id).to.equal @projectId.toString()
  559. expect(@ProjectController.loadEditor.callCount).to.equal 1
  560. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  561. done()
  562. describe 'when the user is already the owner', ->
  563. beforeEach ->
  564. @req = new MockRequest()
  565. @res = new MockResponse()
  566. @next = sinon.stub()
  567. @req.params['read_only_token'] = @readOnlyToken
  568. @project.owner_ref = @userId
  569. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  570. .callsArgWith(1, null, @project, true)
  571. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  572. .callsArgWith(2, null)
  573. @ProjectController.loadEditor = sinon.stub()
  574. @TokenAccessController.readOnlyToken @req, @res, @next
  575. it 'should try to find a project with this token', (done) ->
  576. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  577. .to.equal 1
  578. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  579. .to.equal true
  580. done()
  581. it 'should not add the user to the project with read-only access', (done) ->
  582. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  583. .to.equal 0
  584. done()
  585. it 'should pass control to loadEditor', (done) ->
  586. expect(@req.params.Project_id).to.equal @projectId.toString()
  587. expect(@ProjectController.loadEditor.callCount).to.equal 1
  588. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  589. done()
  590. describe 'when findProject produces an error', ->
  591. beforeEach ->
  592. @req = new MockRequest()
  593. @res = new MockResponse()
  594. @next = sinon.stub()
  595. @req.params['read_only_token'] = @readOnlyToken
  596. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  597. .callsArgWith(1, new Error('woops'))
  598. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  599. .callsArgWith(2, null)
  600. @ProjectController.loadEditor = sinon.stub()
  601. @TokenAccessController.readOnlyToken @req, @res, @next
  602. it 'should try to find a project with this token', (done) ->
  603. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  604. .to.equal 1
  605. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  606. .to.equal true
  607. done()
  608. it 'should not add the user to the project with read-only access', (done) ->
  609. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  610. .to.equal 0
  611. done()
  612. it 'should not pass control to loadEditor', (done) ->
  613. expect(@ProjectController.loadEditor.callCount).to.equal 0
  614. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  615. done()
  616. it 'should call next with an error', (done) ->
  617. expect(@next.callCount).to.equal 1
  618. expect(@next.lastCall.args[0]).to.be.instanceof Error
  619. done()
  620. describe 'when findProject does not find a project', ->
  621. describe 'when project does not exist', ->
  622. beforeEach ->
  623. @req = new MockRequest()
  624. @res = new MockResponse()
  625. @res.redirect = sinon.stub()
  626. @next = sinon.stub()
  627. @req.params['read_only_token'] = 'abcd'
  628. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  629. .callsArgWith(1, null, null, false)
  630. @TokenAccessHandler.checkV1ProjectExported = sinon.stub()
  631. .callsArgWith(1, null, false)
  632. @TokenAccessController.readOnlyToken @req, @res, @next
  633. it 'should redirect to v1', (done) ->
  634. expect(@res.redirect.callCount).to.equal 1
  635. expect(@res.redirect.calledWith(
  636. 302,
  637. '/sign_in_to_v1?return_to=/read/abcd'
  638. )).to.equal true
  639. done()
  640. describe 'when project was not exported from v1 but forcing import to v2', ->
  641. beforeEach ->
  642. @Features.hasFeature.returns(true)
  643. @req = new MockRequest()
  644. @res = new MockResponse()
  645. @res.render = sinon.stub()
  646. @next = sinon.stub()
  647. @req.params['read_only_token'] = 'abcd'
  648. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  649. .callsArgWith(1, null, null, false)
  650. describe 'with project name', ->
  651. beforeEach ->
  652. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  653. exists: true
  654. exported: false
  655. has_owner: true
  656. name: 'A title'
  657. has_assignment: false
  658. brand_info: null
  659. })
  660. @TokenAccessController.readOnlyToken @req, @res, @next
  661. it 'should render v2-import page with name', (done) ->
  662. expect(@res.render.calledWith(
  663. 'project/v2-import',
  664. {
  665. projectId: 'abcd'
  666. name: 'A title'
  667. hasOwner: true
  668. hasAssignment: false
  669. brandInfo: null
  670. }
  671. )).to.equal true
  672. done()
  673. describe 'with project owner', ->
  674. beforeEach ->
  675. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  676. exists: true
  677. exported: false
  678. has_owner: true
  679. name: 'A title'
  680. has_assignment: false
  681. brand_info: null
  682. })
  683. @TokenAccessController.readOnlyToken @req, @res, @next
  684. it 'should render v2-import page', (done) ->
  685. expect(@res.render.calledWith(
  686. 'project/v2-import',
  687. {
  688. projectId: 'abcd',
  689. hasOwner: true
  690. name: 'A title'
  691. hasAssignment: false
  692. brandInfo: null
  693. }
  694. )).to.equal true
  695. done()
  696. describe 'without project owner', ->
  697. beforeEach ->
  698. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  699. exists: true
  700. exported: false
  701. has_owner: false
  702. name: 'A title'
  703. has_assignment: false
  704. brand_info: null
  705. })
  706. @TokenAccessController.readOnlyToken @req, @res, @next
  707. it 'should render v2-import page', (done) ->
  708. expect(@res.render.calledWith(
  709. 'project/v2-import',
  710. {
  711. projectId: 'abcd',
  712. hasOwner: false
  713. name: 'A title'
  714. hasAssignment: false
  715. brandInfo: null
  716. }
  717. )).to.equal true
  718. done()
  719. describe 'with assignment', ->
  720. beforeEach ->
  721. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  722. exists: true
  723. exported: false
  724. has_owner: false
  725. name: 'A title'
  726. has_assignment: true
  727. brand_info: null
  728. })
  729. @TokenAccessController.readOnlyToken @req, @res, @next
  730. it 'should render v2-import page', (done) ->
  731. expect(@res.render.calledWith(
  732. 'project/v2-import',
  733. {
  734. projectId: 'abcd',
  735. hasOwner: false
  736. name: 'A title'
  737. hasAssignment: true
  738. brandInfo: null
  739. }
  740. )).to.equal true
  741. done()
  742. describe 'with brand info', ->
  743. beforeEach ->
  744. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  745. exists: true
  746. exported: false
  747. has_owner: false
  748. name: 'A title'
  749. has_assignment: false
  750. brand_info: 'f1000'
  751. })
  752. @TokenAccessController.readOnlyToken @req, @res, @next
  753. it 'should render v2-import page', (done) ->
  754. expect(@res.render.calledWith(
  755. 'project/v2-import',
  756. {
  757. projectId: 'abcd',
  758. hasOwner: false
  759. name: 'A title'
  760. hasAssignment: false
  761. brandInfo: 'f1000'
  762. }
  763. )).to.equal true
  764. done()
  765. describe 'when project was exported from v1', ->
  766. beforeEach ->
  767. @req = new MockRequest()
  768. @res = new MockResponse()
  769. @res.redirect = sinon.stub()
  770. @next = sinon.stub()
  771. @req.params['read_only_token'] = 'abcd'
  772. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  773. .callsArgWith(1, null, null, false)
  774. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  775. allow: true
  776. exists: true
  777. exported: true
  778. })
  779. @TokenAccessController.readOnlyToken @req, @res, @next
  780. it 'should call next with a not-found error', (done) ->
  781. expect(@next.callCount).to.equal 1
  782. done()
  783. describe 'when token access is off, but user has higher access anyway', ->
  784. beforeEach ->
  785. @req = new MockRequest()
  786. @res = new MockResponse()
  787. @res.redirect = sinon.stub()
  788. @next = sinon.stub()
  789. @req.params['read_and_write_token'] = @readAndWriteToken
  790. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  791. .callsArgWith(1, null, null, true)
  792. @TokenAccessHandler.findProjectWithHigherAccess =
  793. sinon.stub()
  794. .callsArgWith(2, null, @project)
  795. @TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
  796. .callsArgWith(2, null)
  797. @ProjectController.loadEditor = sinon.stub()
  798. @TokenAccessController.readAndWriteToken @req, @res, @next
  799. it 'should try to find a project with this token', (done) ->
  800. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  801. .to.equal 1
  802. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(@readAndWriteToken))
  803. .to.equal true
  804. done()
  805. it 'should check if user has higher access to the token project', (done) ->
  806. expect(
  807. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  808. ).to.equal 1
  809. done()
  810. it 'should not add the user to the project with read-write access', (done) ->
  811. expect(@TokenAccessHandler.addReadAndWriteUserToProject.callCount)
  812. .to.equal 0
  813. done()
  814. it 'should not pass control to loadEditor', (done) ->
  815. expect(@ProjectController.loadEditor.callCount).to.equal 0
  816. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  817. done()
  818. it 'should not call next with a not-found error', (done) ->
  819. expect(@next.callCount).to.equal 0
  820. done()
  821. it 'should redirect to the canonical project url', (done) ->
  822. expect(@res.redirect.callCount).to.equal 1
  823. expect(@res.redirect.calledWith(302, "/project/#{@project._id}")).to.equal true
  824. done()
  825. describe 'when higher access is not available', ->
  826. beforeEach ->
  827. @req = new MockRequest()
  828. @res = new MockResponse()
  829. @next = sinon.stub()
  830. @req.params['read_and_write_token'] = @readAndWriteToken
  831. @TokenAccessHandler.findProjectWithReadAndWriteToken = sinon.stub()
  832. .callsArgWith(1, null, null, true)
  833. @TokenAccessHandler.findProjectWithHigherAccess =
  834. sinon.stub()
  835. .callsArgWith(2, null, null)
  836. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  837. .callsArgWith(2, null)
  838. @ProjectController.loadEditor = sinon.stub()
  839. @TokenAccessController.readAndWriteToken @req, @res, @next
  840. it 'should try to find a project with this token', (done) ->
  841. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.callCount)
  842. .to.equal 1
  843. expect(@TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  844. @readAndWriteToken
  845. )).to.equal true
  846. done()
  847. it 'should check if user has higher access to the token project', (done) ->
  848. expect(
  849. @TokenAccessHandler.findProjectWithHigherAccess.callCount
  850. ).to.equal 1
  851. done()
  852. it 'should not add the user to the project with read-write access', (done) ->
  853. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  854. .to.equal 0
  855. done()
  856. it 'should not pass control to loadEditor', (done) ->
  857. expect(@ProjectController.loadEditor.callCount).to.equal 0
  858. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  859. done()
  860. it 'should call next with a not-found error', (done) ->
  861. expect(@next.callCount).to.equal 1
  862. expect(@next.lastCall.args[0]).to.be.instanceof Error
  863. done()
  864. describe 'when adding user to project produces an error', ->
  865. beforeEach ->
  866. @req = new MockRequest()
  867. @res = new MockResponse()
  868. @next = sinon.stub()
  869. @req.params['read_only_token'] = @readOnlyToken
  870. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  871. .callsArgWith(1, null, @project, true)
  872. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  873. .callsArgWith(2, new Error('woops'))
  874. @ProjectController.loadEditor = sinon.stub()
  875. @TokenAccessController.readOnlyToken @req, @res, @next
  876. it 'should try to find a project with this token', (done) ->
  877. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  878. .to.equal 1
  879. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  880. .to.equal true
  881. done()
  882. it 'should add the user to the project with read-only access', (done) ->
  883. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  884. .to.equal 1
  885. expect(@TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  886. @userId.toString(), @projectId
  887. ))
  888. .to.equal true
  889. done()
  890. it 'should not pass control to loadEditor', (done) ->
  891. expect(@ProjectController.loadEditor.callCount).to.equal 0
  892. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  893. done()
  894. it 'should call next with an error', (done) ->
  895. expect(@next.callCount).to.equal 1
  896. expect(@next.lastCall.args[0]).to.be.instanceof Error
  897. done()
  898. describe 'anonymous', ->
  899. beforeEach ->
  900. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
  901. @TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  902. describe 'when all goes well', ->
  903. beforeEach ->
  904. @req = new MockRequest()
  905. @res = new MockResponse()
  906. @next = sinon.stub()
  907. @req.params['read_only_token'] = @readOnlyToken
  908. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  909. .callsArgWith(1, null, @project, true)
  910. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  911. .callsArgWith(2, null)
  912. @ProjectController.loadEditor = sinon.stub()
  913. @TokenAccessController.readOnlyToken @req, @res, @next
  914. it 'should try to find a project with this token', (done) ->
  915. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  916. .to.equal 1
  917. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  918. .to.equal true
  919. done()
  920. it 'should give the user session read-only access', (done) ->
  921. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  922. .to.equal 1
  923. expect(@TokenAccessHandler.grantSessionTokenAccess.calledWith(
  924. @req, @projectId, @readOnlyToken
  925. ))
  926. .to.equal true
  927. done()
  928. it 'should not add the user to the project with read-only access', (done) ->
  929. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  930. .to.equal 0
  931. done()
  932. it 'should pass control to loadEditor', (done) ->
  933. expect(@req.params.Project_id).to.equal @projectId.toString()
  934. expect(@req._anonymousAccessToken).to.equal @readOnlyToken
  935. expect(@ProjectController.loadEditor.callCount).to.equal 1
  936. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal true
  937. done()
  938. describe 'when findProject produces an error', ->
  939. beforeEach ->
  940. @req = new MockRequest()
  941. @res = new MockResponse()
  942. @next = sinon.stub()
  943. @req.params['read_only_token'] = @readOnlyToken
  944. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  945. .callsArgWith(1, new Error('woops'))
  946. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  947. .callsArgWith(2, null)
  948. @ProjectController.loadEditor = sinon.stub()
  949. @TokenAccessController.readOnlyToken @req, @res, @next
  950. it 'should try to find a project with this token', (done) ->
  951. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  952. .to.equal 1
  953. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  954. .to.equal true
  955. done()
  956. it 'should not give the user session read-only access', (done) ->
  957. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  958. .to.equal 0
  959. done()
  960. it 'should not add the user to the project with read-only access', (done) ->
  961. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  962. .to.equal 0
  963. done()
  964. it 'should not pass control to loadEditor', (done) ->
  965. expect(@ProjectController.loadEditor.callCount).to.equal 0
  966. expect(@ProjectController.loadEditor.calledWith(@req, @res, @next)).to.equal false
  967. done()
  968. it 'should call next with an error', (done) ->
  969. expect(@next.callCount).to.equal 1
  970. expect(@next.lastCall.args[0]).to.be.instanceof Error
  971. done()
  972. describe 'when findProject does not find a project', ->
  973. beforeEach ->
  974. @req = new MockRequest()
  975. @res = new MockResponse()
  976. @res.redirect = sinon.stub()
  977. @next = sinon.stub()
  978. @req.params['read_only_token'] = @readOnlyToken
  979. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
  980. @TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
  981. .callsArgWith(1, null, false)
  982. @TokenAccessHandler.addReadOnlyUserToProject = sinon.stub()
  983. describe 'when project does not exist', ->
  984. beforeEach ->
  985. @TokenAccessController.readOnlyToken @req, @res, @next
  986. it 'should try to find a project with this token', (done) ->
  987. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.callCount)
  988. .to.equal 1
  989. expect(@TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(@readOnlyToken))
  990. .to.equal true
  991. done()
  992. it 'should not give the user session read-only access', (done) ->
  993. expect(@TokenAccessHandler.grantSessionTokenAccess.callCount)
  994. .to.equal 0
  995. done()
  996. it 'should not add the user to the project with read-only access', (done) ->
  997. expect(@TokenAccessHandler.addReadOnlyUserToProject.callCount)
  998. .to.equal 0
  999. done()
  1000. describe 'when project was exported to v2', ->
  1001. beforeEach ->
  1002. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1003. exists: true
  1004. exported: true
  1005. })
  1006. @TokenAccessController.readOnlyToken @req, @res, @next
  1007. it 'should call next with not found error', (done) ->
  1008. expect(@next.callCount).to.equal 1
  1009. expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
  1010. done()
  1011. describe 'when project was not exported to v2', ->
  1012. beforeEach ->
  1013. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1014. exists: true
  1015. exported: false
  1016. })
  1017. @TokenAccessController.readOnlyToken @req, @res, @next
  1018. it 'should redirect to v1', (done) ->
  1019. expect(@res.redirect.callCount).to.equal 1
  1020. expect(@res.redirect.calledWith(
  1021. 302,
  1022. "/sign_in_to_v1?return_to=/read/#{@readOnlyToken}"
  1023. )).to.equal true
  1024. done()
  1025. describe 'when project does not exist on v1', ->
  1026. beforeEach ->
  1027. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1028. exists: false,
  1029. exported: false
  1030. })
  1031. @TokenAccessController.readOnlyToken @req, @res, @next
  1032. it 'should call next with not found error', (done) ->
  1033. expect(@next.callCount).to.equal 1
  1034. expect(@next.calledWith(new Errors.NotFoundError())).to.equal true
  1035. done()
  1036. describe 'anonymous user', ->
  1037. beforeEach ->
  1038. @AuthenticationController.getLoggedInUserId = sinon.stub().returns(null)
  1039. describe 'when project was not exported to v2', ->
  1040. beforeEach ->
  1041. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1042. exists: true
  1043. exported: false
  1044. })
  1045. @TokenAccessController.readOnlyToken @req, @res, @next
  1046. it 'should redirect to v1', (done) ->
  1047. expect(@res.redirect.callCount).to.equal 1
  1048. expect(@res.redirect.calledWith(
  1049. 302,
  1050. "/sign_in_to_v1?return_to=/read/#{@readOnlyToken}"
  1051. )).to.equal true
  1052. done()
  1053. describe 'force-import-to-v2 flag is on', ->
  1054. beforeEach ->
  1055. @res.render = sinon.stub()
  1056. @Features.hasFeature.returns(true)
  1057. describe 'when project was not exported to v2', ->
  1058. beforeEach ->
  1059. @TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1060. exists: true
  1061. exported: false
  1062. })
  1063. @TokenAccessController.readOnlyToken @req, @res, @next
  1064. it 'should render anonymous import status page', (done) ->
  1065. expect(@res.render.callCount).to.equal 1
  1066. expect(@res.render.calledWith(
  1067. 'project/v2-import',
  1068. { loginRedirect: "/read/#{@readOnlyToken}" }
  1069. )).to.equal true
  1070. done()