TokenAccessControllerTests.js 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const should = require('chai').should()
  14. const SandboxedModule = require('sandboxed-module')
  15. const assert = require('assert')
  16. const path = require('path')
  17. const sinon = require('sinon')
  18. const modulePath = path.join(
  19. __dirname,
  20. '../../../../app/src/Features/TokenAccess/TokenAccessController'
  21. )
  22. const { expect } = require('chai')
  23. const { ObjectId } = require('mongojs')
  24. const MockRequest = require('../helpers/MockRequest')
  25. const MockResponse = require('../helpers/MockResponse')
  26. const Errors = require('../../../../app/src/Features/Errors/Errors.js')
  27. describe('TokenAccessController', function() {
  28. beforeEach(function() {
  29. this.readOnlyToken = 'somereadonlytoken'
  30. this.readAndWriteToken = '42somereadandwritetoken'
  31. this.projectId = ObjectId()
  32. this.ownerId = 'owner'
  33. this.project = {
  34. _id: this.projectId,
  35. publicAccesLevel: 'tokenBased',
  36. tokens: {
  37. readOnly: this.readOnlyToken,
  38. readAndWrite: this.readAndWriteToken
  39. },
  40. owner_ref: this.ownerId
  41. }
  42. this.userId = ObjectId()
  43. this.TokenAccessController = SandboxedModule.require(modulePath, {
  44. globals: {
  45. console: console
  46. },
  47. requires: {
  48. '../Project/ProjectController': (this.ProjectController = {}),
  49. '../Authentication/AuthenticationController': (this.AuthenticationController = {}),
  50. './TokenAccessHandler': (this.TokenAccessHandler = {
  51. getV1DocPublishedInfo: sinon.stub().yields(null, {
  52. allow: true
  53. }),
  54. getV1DocInfo: sinon.stub().yields(null, {
  55. exists: true,
  56. exported: false
  57. })
  58. }),
  59. '../../infrastructure/Features': (this.Features = {
  60. hasFeature: sinon.stub().returns(false)
  61. }),
  62. 'logger-sharelatex': {
  63. log: sinon.stub(),
  64. warn: sinon.stub(),
  65. err: sinon.stub()
  66. },
  67. 'settings-sharelatex': {
  68. overleaf: {
  69. host: 'http://overleaf.test:5000'
  70. }
  71. },
  72. '../V1/V1Api': (this.V1Api = {
  73. request: sinon.stub().callsArgWith(1, null, {}, { allow: true })
  74. }),
  75. '../Errors/Errors': Errors
  76. }
  77. })
  78. return (this.AuthenticationController.getLoggedInUserId = sinon
  79. .stub()
  80. .returns(this.userId.toString()))
  81. })
  82. describe('readAndWriteToken', function() {
  83. beforeEach(function() {})
  84. describe('when all goes well', function() {
  85. beforeEach(function() {
  86. this.req = new MockRequest()
  87. this.res = new MockResponse()
  88. this.next = sinon.stub()
  89. this.req.params['read_and_write_token'] = this.readAndWriteToken
  90. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  91. .stub()
  92. .callsArgWith(1, null, this.project, true)
  93. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  94. .stub()
  95. .callsArgWith(2, null)
  96. this.ProjectController.loadEditor = sinon.stub()
  97. this.AuthenticationController.setRedirectInSession = sinon.stub()
  98. return this.TokenAccessController.readAndWriteToken(
  99. this.req,
  100. this.res,
  101. this.next
  102. )
  103. })
  104. it('should try to find a project with this token', function(done) {
  105. expect(
  106. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  107. ).to.equal(1)
  108. expect(
  109. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  110. this.readAndWriteToken
  111. )
  112. ).to.equal(true)
  113. return done()
  114. })
  115. it('should add the user to the project with read-write access', function(done) {
  116. expect(
  117. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  118. ).to.equal(1)
  119. expect(
  120. this.TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  121. this.userId.toString(),
  122. this.projectId
  123. )
  124. ).to.equal(true)
  125. return done()
  126. })
  127. it('should pass control to loadEditor', function(done) {
  128. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  129. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  130. expect(
  131. this.ProjectController.loadEditor.calledWith(
  132. this.req,
  133. this.res,
  134. this.next
  135. )
  136. ).to.equal(true)
  137. return done()
  138. })
  139. })
  140. describe('when the user is already the owner', function() {
  141. beforeEach(function() {
  142. this.req = new MockRequest()
  143. this.res = new MockResponse()
  144. this.next = sinon.stub()
  145. this.req.params['read_and_write_token'] = this.readAndWriteToken
  146. this.project.owner_ref = this.userId
  147. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  148. .stub()
  149. .callsArgWith(1, null, this.project, true)
  150. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  151. .stub()
  152. .callsArgWith(2, null)
  153. this.ProjectController.loadEditor = sinon.stub()
  154. return this.TokenAccessController.readAndWriteToken(
  155. this.req,
  156. this.res,
  157. this.next
  158. )
  159. })
  160. it('should try to find a project with this token', function(done) {
  161. expect(
  162. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  163. ).to.equal(1)
  164. expect(
  165. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  166. this.readAndWriteToken
  167. )
  168. ).to.equal(true)
  169. return done()
  170. })
  171. it('should not add the user to the project with read-write access', function(done) {
  172. expect(
  173. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  174. ).to.equal(0)
  175. return done()
  176. })
  177. it('should pass control to loadEditor', function(done) {
  178. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  179. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  180. expect(
  181. this.ProjectController.loadEditor.calledWith(
  182. this.req,
  183. this.res,
  184. this.next
  185. )
  186. ).to.equal(true)
  187. return done()
  188. })
  189. })
  190. describe('when there is no user', function() {
  191. beforeEach(function() {
  192. return (this.AuthenticationController.getLoggedInUserId = sinon
  193. .stub()
  194. .returns(null))
  195. })
  196. describe('when anonymous read-write access is enabled', function() {
  197. beforeEach(function() {
  198. this.TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = true
  199. this.req = new MockRequest()
  200. this.res = new MockResponse()
  201. this.next = sinon.stub()
  202. this.req.params['read_and_write_token'] = this.readAndWriteToken
  203. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  204. .stub()
  205. .callsArgWith(1, null, this.project, true)
  206. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  207. .stub()
  208. .callsArgWith(2, null)
  209. this.ProjectController.loadEditor = sinon.stub()
  210. this.TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  211. return this.TokenAccessController.readAndWriteToken(
  212. this.req,
  213. this.res,
  214. this.next
  215. )
  216. })
  217. it('should not add the user to the project with read-write access', function(done) {
  218. expect(
  219. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  220. ).to.equal(0)
  221. return done()
  222. })
  223. it('should give the user session token access', function(done) {
  224. expect(
  225. this.TokenAccessHandler.grantSessionTokenAccess.callCount
  226. ).to.equal(1)
  227. expect(
  228. this.TokenAccessHandler.grantSessionTokenAccess.calledWith(
  229. this.req,
  230. this.projectId,
  231. this.readAndWriteToken
  232. )
  233. ).to.equal(true)
  234. return done()
  235. })
  236. it('should pass control to loadEditor', function(done) {
  237. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  238. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  239. expect(
  240. this.ProjectController.loadEditor.calledWith(
  241. this.req,
  242. this.res,
  243. this.next
  244. )
  245. ).to.equal(true)
  246. return done()
  247. })
  248. })
  249. describe('when anonymous read-write access is not enabled', function() {
  250. beforeEach(function() {
  251. this.TokenAccessHandler.ANONYMOUS_READ_AND_WRITE_ENABLED = false
  252. this.req = new MockRequest()
  253. this.res = new MockResponse()
  254. this.res.redirect = sinon.stub()
  255. this.next = sinon.stub()
  256. this.req.params['read_and_write_token'] = this.readAndWriteToken
  257. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  258. .stub()
  259. .callsArgWith(1, null, this.project, true)
  260. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  261. .stub()
  262. .callsArgWith(2, null)
  263. this.ProjectController.loadEditor = sinon.stub()
  264. this.TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
  265. this.AuthenticationController.setRedirectInSession = sinon.stub()
  266. return this.TokenAccessController.readAndWriteToken(
  267. this.req,
  268. this.res,
  269. this.next
  270. )
  271. })
  272. it('should not add the user to the project with read-write access', function(done) {
  273. expect(
  274. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  275. ).to.equal(0)
  276. return done()
  277. })
  278. it('should give the user session token access', function(done) {
  279. expect(
  280. this.TokenAccessHandler.grantSessionTokenAccess.callCount
  281. ).to.equal(0)
  282. return done()
  283. })
  284. it('should not pass control to loadEditor', function(done) {
  285. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  286. expect(
  287. this.ProjectController.loadEditor.calledWith(
  288. this.req,
  289. this.res,
  290. this.next
  291. )
  292. ).to.equal(false)
  293. return done()
  294. })
  295. it('should set redirect in session', function(done) {
  296. expect(
  297. this.AuthenticationController.setRedirectInSession.callCount
  298. ).to.equal(1)
  299. expect(
  300. this.AuthenticationController.setRedirectInSession.calledWith(
  301. this.req
  302. )
  303. ).to.equal(true)
  304. return done()
  305. })
  306. it('should redirect to restricted page', function(done) {
  307. expect(this.res.redirect.callCount).to.equal(1)
  308. expect(this.res.redirect.calledWith('/restricted')).to.equal(true)
  309. return done()
  310. })
  311. })
  312. })
  313. describe('when findProject produces an error', function() {
  314. beforeEach(function() {
  315. this.req = new MockRequest()
  316. this.res = new MockResponse()
  317. this.next = sinon.stub()
  318. this.req.params['read_and_write_token'] = this.readAndWriteToken
  319. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  320. .stub()
  321. .callsArgWith(1, new Error('woops'))
  322. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  323. .stub()
  324. .callsArgWith(2, null)
  325. this.ProjectController.loadEditor = sinon.stub()
  326. return this.TokenAccessController.readAndWriteToken(
  327. this.req,
  328. this.res,
  329. this.next
  330. )
  331. })
  332. it('should try to find a project with this token', function(done) {
  333. expect(
  334. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  335. ).to.equal(1)
  336. expect(
  337. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  338. this.readAndWriteToken
  339. )
  340. ).to.equal(true)
  341. return done()
  342. })
  343. it('should not add the user to the project with read-write access', function(done) {
  344. expect(
  345. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  346. ).to.equal(0)
  347. return done()
  348. })
  349. it('should not pass control to loadEditor', function(done) {
  350. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  351. expect(
  352. this.ProjectController.loadEditor.calledWith(
  353. this.req,
  354. this.res,
  355. this.next
  356. )
  357. ).to.equal(false)
  358. return done()
  359. })
  360. it('should call next with an error', function(done) {
  361. expect(this.next.callCount).to.equal(1)
  362. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  363. return done()
  364. })
  365. })
  366. describe('when findProject does not find a project', function() {
  367. beforeEach(function() {})
  368. describe('when user is present', function() {
  369. beforeEach(function() {
  370. return (this.AuthenticationController.getLoggedInUserId = sinon
  371. .stub()
  372. .returns(this.userId.toString()))
  373. })
  374. describe('when project does not exist', function() {
  375. beforeEach(function() {
  376. this.req = new MockRequest()
  377. this.req.url = '/123abc'
  378. this.res = new MockResponse()
  379. this.res.redirect = sinon.stub()
  380. this.res.render = sinon.stub()
  381. this.next = sinon.stub()
  382. this.req.params['read_and_write_token'] = '123abc'
  383. return (this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  384. .stub()
  385. .callsArgWith(1, null, null, false))
  386. })
  387. describe('when project was not exported from v1', function() {
  388. beforeEach(function() {
  389. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  390. exists: true,
  391. exported: false
  392. })
  393. return this.TokenAccessController.readAndWriteToken(
  394. this.req,
  395. this.res,
  396. this.next
  397. )
  398. })
  399. it('should not redirect to v1', function(done) {
  400. expect(this.res.redirect.callCount).to.equal(0)
  401. done()
  402. })
  403. it('should show project import page', function(done) {
  404. expect(this.res.render.calledWith('project/v2-import')).to.equal(
  405. true
  406. )
  407. done()
  408. })
  409. })
  410. describe('when project was not exported from v1 but forcing import to v2', function() {
  411. beforeEach(function() {
  412. return this.Features.hasFeature.returns(true)
  413. })
  414. describe('with project name', function() {
  415. beforeEach(function() {
  416. this.TokenAccessHandler.getV1DocInfo = sinon
  417. .stub()
  418. .yields(null, {
  419. exists: true,
  420. exported: false,
  421. has_owner: true,
  422. name: 'A title',
  423. has_assignment: false,
  424. brand_info: null
  425. })
  426. return this.TokenAccessController.readAndWriteToken(
  427. this.req,
  428. this.res,
  429. this.next
  430. )
  431. })
  432. it('should render v2-import page with name', function(done) {
  433. expect(
  434. this.res.render.calledWith('project/v2-import', {
  435. projectId: '123abc',
  436. name: 'A title',
  437. hasOwner: true,
  438. hasAssignment: false,
  439. brandInfo: null
  440. })
  441. ).to.equal(true)
  442. return done()
  443. })
  444. })
  445. describe('with project owner', function() {
  446. beforeEach(function() {
  447. this.TokenAccessHandler.getV1DocInfo = sinon
  448. .stub()
  449. .yields(null, {
  450. exists: true,
  451. exported: false,
  452. has_owner: true,
  453. name: 'A title',
  454. has_assignment: false,
  455. brand_info: null
  456. })
  457. return this.TokenAccessController.readAndWriteToken(
  458. this.req,
  459. this.res,
  460. this.next
  461. )
  462. })
  463. it('should render v2-import page', function(done) {
  464. expect(
  465. this.res.render.calledWith('project/v2-import', {
  466. projectId: '123abc',
  467. hasOwner: true,
  468. name: 'A title',
  469. hasAssignment: false,
  470. brandInfo: null
  471. })
  472. ).to.equal(true)
  473. return done()
  474. })
  475. })
  476. describe('without project owner', function() {
  477. beforeEach(function() {
  478. this.TokenAccessHandler.getV1DocInfo = sinon
  479. .stub()
  480. .yields(null, {
  481. exists: true,
  482. exported: false,
  483. has_owner: false,
  484. name: 'A title',
  485. has_assignment: false,
  486. brand_info: null
  487. })
  488. return this.TokenAccessController.readAndWriteToken(
  489. this.req,
  490. this.res,
  491. this.next
  492. )
  493. })
  494. it('should render v2-import page', function(done) {
  495. expect(
  496. this.res.render.calledWith('project/v2-import', {
  497. projectId: '123abc',
  498. hasOwner: false,
  499. name: 'A title',
  500. hasAssignment: false,
  501. brandInfo: null
  502. })
  503. ).to.equal(true)
  504. return done()
  505. })
  506. })
  507. describe('with assignment', function() {
  508. beforeEach(function() {
  509. this.TokenAccessHandler.getV1DocInfo = sinon
  510. .stub()
  511. .yields(null, {
  512. exists: true,
  513. exported: false,
  514. has_owner: false,
  515. name: 'A title',
  516. has_assignment: true,
  517. brand_info: null
  518. })
  519. return this.TokenAccessController.readAndWriteToken(
  520. this.req,
  521. this.res,
  522. this.next
  523. )
  524. })
  525. it('should render v2-import page', function(done) {
  526. expect(
  527. this.res.render.calledWith('project/v2-import', {
  528. projectId: '123abc',
  529. hasOwner: false,
  530. name: 'A title',
  531. hasAssignment: true,
  532. brandInfo: null
  533. })
  534. ).to.equal(true)
  535. return done()
  536. })
  537. })
  538. describe('with brand info', function() {
  539. beforeEach(function() {
  540. this.TokenAccessHandler.getV1DocInfo = sinon
  541. .stub()
  542. .yields(null, {
  543. exists: true,
  544. exported: false,
  545. has_owner: false,
  546. name: 'A title',
  547. has_assignment: false,
  548. brand_info: 'wellcome'
  549. })
  550. return this.TokenAccessController.readAndWriteToken(
  551. this.req,
  552. this.res,
  553. this.next
  554. )
  555. })
  556. it('should render v2-import page', function(done) {
  557. expect(
  558. this.res.render.calledWith('project/v2-import', {
  559. projectId: '123abc',
  560. hasOwner: false,
  561. name: 'A title',
  562. hasAssignment: false,
  563. brandInfo: 'wellcome'
  564. })
  565. ).to.equal(true)
  566. return done()
  567. })
  568. })
  569. describe('with anonymous user', function() {
  570. beforeEach(function() {
  571. this.AuthenticationController.getLoggedInUserId = sinon
  572. .stub()
  573. .returns(null)
  574. return this.TokenAccessController.readAndWriteToken(
  575. this.req,
  576. this.res,
  577. this.next
  578. )
  579. })
  580. it('should render anonymous import status page', function(done) {
  581. expect(this.res.render.callCount).to.equal(1)
  582. expect(
  583. this.res.render.calledWith('project/v2-import', {
  584. loginRedirect: '/123abc'
  585. })
  586. ).to.equal(true)
  587. return done()
  588. })
  589. })
  590. })
  591. describe('when project was exported from v1', function() {
  592. beforeEach(function() {
  593. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  594. exists: true,
  595. exported: true
  596. })
  597. return this.TokenAccessController.readAndWriteToken(
  598. this.req,
  599. this.res,
  600. this.next
  601. )
  602. })
  603. it('should call next with a not-found error', function(done) {
  604. expect(this.next.callCount).to.equal(1)
  605. return done()
  606. })
  607. })
  608. describe('when project does not exist on v1', function() {
  609. beforeEach(function() {
  610. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  611. exists: false,
  612. exported: false
  613. })
  614. return this.TokenAccessController.readAndWriteToken(
  615. this.req,
  616. this.res,
  617. this.next
  618. )
  619. })
  620. it('should call next with a not-found error', function(done) {
  621. expect(this.next.callCount).to.equal(1)
  622. expect(
  623. this.next.calledWith(
  624. sinon.match.instanceOf(Errors.NotFoundError)
  625. )
  626. ).to.equal(true)
  627. return done()
  628. })
  629. })
  630. })
  631. describe('when token access is off, but user has higher access anyway', function() {
  632. beforeEach(function() {
  633. this.req = new MockRequest()
  634. this.res = new MockResponse()
  635. this.res.redirect = sinon.stub()
  636. this.next = sinon.stub()
  637. this.req.params['read_and_write_token'] = this.readAndWriteToken
  638. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  639. .stub()
  640. .callsArgWith(1, null, null, true)
  641. this.TokenAccessHandler.findProjectWithHigherAccess = sinon
  642. .stub()
  643. .callsArgWith(2, null, this.project)
  644. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  645. .stub()
  646. .callsArgWith(2, null)
  647. this.ProjectController.loadEditor = sinon.stub()
  648. return this.TokenAccessController.readAndWriteToken(
  649. this.req,
  650. this.res,
  651. this.next
  652. )
  653. })
  654. it('should try to find a project with this token', function(done) {
  655. expect(
  656. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  657. ).to.equal(1)
  658. expect(
  659. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  660. this.readAndWriteToken
  661. )
  662. ).to.equal(true)
  663. return done()
  664. })
  665. it('should check if user has higher access to the token project', function(done) {
  666. expect(
  667. this.TokenAccessHandler.findProjectWithHigherAccess.callCount
  668. ).to.equal(1)
  669. return done()
  670. })
  671. it('should not add the user to the project with read-write access', function(done) {
  672. expect(
  673. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  674. ).to.equal(0)
  675. return done()
  676. })
  677. it('should not pass control to loadEditor', function(done) {
  678. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  679. expect(
  680. this.ProjectController.loadEditor.calledWith(
  681. this.req,
  682. this.res,
  683. this.next
  684. )
  685. ).to.equal(false)
  686. return done()
  687. })
  688. it('should not call next with a not-found error', function(done) {
  689. expect(this.next.callCount).to.equal(0)
  690. return done()
  691. })
  692. it('should redirect to the canonical project url', function(done) {
  693. expect(this.res.redirect.callCount).to.equal(1)
  694. expect(
  695. this.res.redirect.calledWith(302, `/project/${this.project._id}`)
  696. ).to.equal(true)
  697. return done()
  698. })
  699. })
  700. describe('when higher access is not available', function() {
  701. beforeEach(function() {
  702. this.req = new MockRequest()
  703. this.res = new MockResponse()
  704. this.next = sinon.stub()
  705. this.req.params['read_and_write_token'] = this.readAndWriteToken
  706. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  707. .stub()
  708. .callsArgWith(1, null, null, true)
  709. this.TokenAccessHandler.findProjectWithHigherAccess = sinon
  710. .stub()
  711. .callsArgWith(2, null, null)
  712. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  713. .stub()
  714. .callsArgWith(2, null)
  715. this.ProjectController.loadEditor = sinon.stub()
  716. return this.TokenAccessController.readAndWriteToken(
  717. this.req,
  718. this.res,
  719. this.next
  720. )
  721. })
  722. it('should try to find a project with this token', function(done) {
  723. expect(
  724. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  725. ).to.equal(1)
  726. expect(
  727. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  728. this.readAndWriteToken
  729. )
  730. ).to.equal(true)
  731. return done()
  732. })
  733. it('should check if user has higher access to the token project', function(done) {
  734. expect(
  735. this.TokenAccessHandler.findProjectWithHigherAccess.callCount
  736. ).to.equal(1)
  737. return done()
  738. })
  739. it('should not add the user to the project with read-write access', function(done) {
  740. expect(
  741. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  742. ).to.equal(0)
  743. return done()
  744. })
  745. it('should not pass control to loadEditor', function(done) {
  746. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  747. expect(
  748. this.ProjectController.loadEditor.calledWith(
  749. this.req,
  750. this.res,
  751. this.next
  752. )
  753. ).to.equal(false)
  754. return done()
  755. })
  756. it('should call next with a not-found error', function(done) {
  757. expect(this.next.callCount).to.equal(1)
  758. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  759. return done()
  760. })
  761. })
  762. })
  763. })
  764. describe('when adding user to project produces an error', function() {
  765. beforeEach(function() {
  766. this.req = new MockRequest()
  767. this.res = new MockResponse()
  768. this.next = sinon.stub()
  769. this.req.params['read_and_write_token'] = this.readAndWriteToken
  770. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  771. .stub()
  772. .callsArgWith(1, null, this.project, true)
  773. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  774. .stub()
  775. .callsArgWith(2, new Error('woops'))
  776. this.ProjectController.loadEditor = sinon.stub()
  777. return this.TokenAccessController.readAndWriteToken(
  778. this.req,
  779. this.res,
  780. this.next
  781. )
  782. })
  783. it('should try to find a project with this token', function(done) {
  784. expect(
  785. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  786. ).to.equal(1)
  787. expect(
  788. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  789. this.readAndWriteToken
  790. )
  791. ).to.equal(true)
  792. return done()
  793. })
  794. it('should add the user to the project with read-write access', function(done) {
  795. expect(
  796. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  797. ).to.equal(1)
  798. expect(
  799. this.TokenAccessHandler.addReadAndWriteUserToProject.calledWith(
  800. this.userId.toString(),
  801. this.projectId
  802. )
  803. ).to.equal(true)
  804. return done()
  805. })
  806. it('should not pass control to loadEditor', function(done) {
  807. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  808. expect(
  809. this.ProjectController.loadEditor.calledWith(
  810. this.req,
  811. this.res,
  812. this.next
  813. )
  814. ).to.equal(false)
  815. return done()
  816. })
  817. it('should call next with an error', function(done) {
  818. expect(this.next.callCount).to.equal(1)
  819. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  820. return done()
  821. })
  822. })
  823. })
  824. describe('readOnlyToken', function() {
  825. beforeEach(function() {
  826. return (this.TokenAccessHandler.checkV1Access = sinon
  827. .stub()
  828. .callsArgWith(1, null, true))
  829. })
  830. describe('when access not allowed by v1 api', function() {
  831. beforeEach(function() {
  832. this.req = new MockRequest()
  833. this.res = new MockResponse()
  834. this.res.redirect = sinon.stub()
  835. this.next = sinon.stub()
  836. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  837. .stub()
  838. .callsArgWith(1, null, this.project, true)
  839. this.TokenAccessHandler.getV1DocPublishedInfo = sinon
  840. .stub()
  841. .yields(null, {
  842. allow: false,
  843. published_path: 'doc-url'
  844. })
  845. return this.TokenAccessController.readOnlyToken(
  846. this.req,
  847. this.res,
  848. this.next
  849. )
  850. })
  851. it('should redirect to doc-url', function() {
  852. return expect(this.res.redirect.calledWith('doc-url')).to.equal(true)
  853. })
  854. })
  855. describe('with a user', function() {
  856. beforeEach(function() {
  857. return (this.AuthenticationController.getLoggedInUserId = sinon
  858. .stub()
  859. .returns(this.userId.toString()))
  860. })
  861. describe('when all goes well', function() {
  862. beforeEach(function() {
  863. this.req = new MockRequest()
  864. this.res = new MockResponse()
  865. this.next = sinon.stub()
  866. this.req.params['read_only_token'] = this.readOnlyToken
  867. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  868. .stub()
  869. .callsArgWith(1, null, this.project, true)
  870. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  871. .stub()
  872. .callsArgWith(2, null)
  873. this.ProjectController.loadEditor = sinon.stub()
  874. return this.TokenAccessController.readOnlyToken(
  875. this.req,
  876. this.res,
  877. this.next
  878. )
  879. })
  880. it('should try to find a project with this token', function(done) {
  881. expect(
  882. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  883. ).to.equal(1)
  884. expect(
  885. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  886. this.readOnlyToken
  887. )
  888. ).to.equal(true)
  889. return done()
  890. })
  891. it('should add the user to the project with read-only access', function(done) {
  892. expect(
  893. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  894. ).to.equal(1)
  895. expect(
  896. this.TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  897. this.userId.toString(),
  898. this.projectId
  899. )
  900. ).to.equal(true)
  901. return done()
  902. })
  903. it('should pass control to loadEditor', function(done) {
  904. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  905. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  906. expect(
  907. this.ProjectController.loadEditor.calledWith(
  908. this.req,
  909. this.res,
  910. this.next
  911. )
  912. ).to.equal(true)
  913. return done()
  914. })
  915. })
  916. describe('when the user is already the owner', function() {
  917. beforeEach(function() {
  918. this.req = new MockRequest()
  919. this.res = new MockResponse()
  920. this.next = sinon.stub()
  921. this.req.params['read_only_token'] = this.readOnlyToken
  922. this.project.owner_ref = this.userId
  923. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  924. .stub()
  925. .callsArgWith(1, null, this.project, true)
  926. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  927. .stub()
  928. .callsArgWith(2, null)
  929. this.ProjectController.loadEditor = sinon.stub()
  930. return this.TokenAccessController.readOnlyToken(
  931. this.req,
  932. this.res,
  933. this.next
  934. )
  935. })
  936. it('should try to find a project with this token', function(done) {
  937. expect(
  938. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  939. ).to.equal(1)
  940. expect(
  941. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  942. this.readOnlyToken
  943. )
  944. ).to.equal(true)
  945. return done()
  946. })
  947. it('should not add the user to the project with read-only access', function(done) {
  948. expect(
  949. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  950. ).to.equal(0)
  951. return done()
  952. })
  953. it('should pass control to loadEditor', function(done) {
  954. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  955. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  956. expect(
  957. this.ProjectController.loadEditor.calledWith(
  958. this.req,
  959. this.res,
  960. this.next
  961. )
  962. ).to.equal(true)
  963. return done()
  964. })
  965. })
  966. describe('when findProject produces an error', function() {
  967. beforeEach(function() {
  968. this.req = new MockRequest()
  969. this.res = new MockResponse()
  970. this.next = sinon.stub()
  971. this.req.params['read_only_token'] = this.readOnlyToken
  972. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  973. .stub()
  974. .callsArgWith(1, new Error('woops'))
  975. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  976. .stub()
  977. .callsArgWith(2, null)
  978. this.ProjectController.loadEditor = sinon.stub()
  979. return this.TokenAccessController.readOnlyToken(
  980. this.req,
  981. this.res,
  982. this.next
  983. )
  984. })
  985. it('should try to find a project with this token', function(done) {
  986. expect(
  987. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  988. ).to.equal(1)
  989. expect(
  990. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  991. this.readOnlyToken
  992. )
  993. ).to.equal(true)
  994. return done()
  995. })
  996. it('should not add the user to the project with read-only access', function(done) {
  997. expect(
  998. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  999. ).to.equal(0)
  1000. return done()
  1001. })
  1002. it('should not pass control to loadEditor', function(done) {
  1003. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  1004. expect(
  1005. this.ProjectController.loadEditor.calledWith(
  1006. this.req,
  1007. this.res,
  1008. this.next
  1009. )
  1010. ).to.equal(false)
  1011. return done()
  1012. })
  1013. it('should call next with an error', function(done) {
  1014. expect(this.next.callCount).to.equal(1)
  1015. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  1016. return done()
  1017. })
  1018. })
  1019. })
  1020. describe('when findProject does not find a project', function() {
  1021. describe('when project does not exist', function() {
  1022. beforeEach(function() {
  1023. this.req = new MockRequest()
  1024. this.res = new MockResponse()
  1025. this.res.redirect = sinon.stub()
  1026. this.res.render = sinon.stub()
  1027. this.next = sinon.stub()
  1028. this.req.params['read_only_token'] = 'abcd'
  1029. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1030. .stub()
  1031. .callsArgWith(1, null, null, false)
  1032. this.TokenAccessHandler.checkV1ProjectExported = sinon
  1033. .stub()
  1034. .callsArgWith(1, null, false)
  1035. this.TokenAccessController.readOnlyToken(
  1036. this.req,
  1037. this.res,
  1038. this.next
  1039. )
  1040. })
  1041. it('should not redirect to v1', function(done) {
  1042. expect(this.res.redirect.callCount).to.equal(0)
  1043. done()
  1044. })
  1045. it('should show project import page', function(done) {
  1046. expect(this.res.render.calledWith('project/v2-import')).to.equal(true)
  1047. done()
  1048. })
  1049. })
  1050. describe('when project was not exported from v1 but forcing import to v2', function() {
  1051. beforeEach(function() {
  1052. this.Features.hasFeature.returns(true)
  1053. this.req = new MockRequest()
  1054. this.res = new MockResponse()
  1055. this.res.render = sinon.stub()
  1056. this.next = sinon.stub()
  1057. this.req.params['read_only_token'] = 'abcd'
  1058. return (this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1059. .stub()
  1060. .callsArgWith(1, null, null, false))
  1061. })
  1062. describe('with project name', function() {
  1063. beforeEach(function() {
  1064. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1065. exists: true,
  1066. exported: false,
  1067. has_owner: true,
  1068. name: 'A title',
  1069. has_assignment: false,
  1070. brand_info: null
  1071. })
  1072. return this.TokenAccessController.readOnlyToken(
  1073. this.req,
  1074. this.res,
  1075. this.next
  1076. )
  1077. })
  1078. it('should render v2-import page with name', function(done) {
  1079. expect(
  1080. this.res.render.calledWith('project/v2-import', {
  1081. projectId: 'abcd',
  1082. name: 'A title',
  1083. hasOwner: true,
  1084. hasAssignment: false,
  1085. brandInfo: null
  1086. })
  1087. ).to.equal(true)
  1088. return done()
  1089. })
  1090. })
  1091. describe('with project owner', function() {
  1092. beforeEach(function() {
  1093. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1094. exists: true,
  1095. exported: false,
  1096. has_owner: true,
  1097. name: 'A title',
  1098. has_assignment: false,
  1099. brand_info: null
  1100. })
  1101. return this.TokenAccessController.readOnlyToken(
  1102. this.req,
  1103. this.res,
  1104. this.next
  1105. )
  1106. })
  1107. it('should render v2-import page', function(done) {
  1108. expect(
  1109. this.res.render.calledWith('project/v2-import', {
  1110. projectId: 'abcd',
  1111. hasOwner: true,
  1112. name: 'A title',
  1113. hasAssignment: false,
  1114. brandInfo: null
  1115. })
  1116. ).to.equal(true)
  1117. return done()
  1118. })
  1119. })
  1120. describe('without project owner', function() {
  1121. beforeEach(function() {
  1122. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1123. exists: true,
  1124. exported: false,
  1125. has_owner: false,
  1126. name: 'A title',
  1127. has_assignment: false,
  1128. brand_info: null
  1129. })
  1130. return this.TokenAccessController.readOnlyToken(
  1131. this.req,
  1132. this.res,
  1133. this.next
  1134. )
  1135. })
  1136. it('should render v2-import page', function(done) {
  1137. expect(
  1138. this.res.render.calledWith('project/v2-import', {
  1139. projectId: 'abcd',
  1140. hasOwner: false,
  1141. name: 'A title',
  1142. hasAssignment: false,
  1143. brandInfo: null
  1144. })
  1145. ).to.equal(true)
  1146. return done()
  1147. })
  1148. })
  1149. describe('with assignment', function() {
  1150. beforeEach(function() {
  1151. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1152. exists: true,
  1153. exported: false,
  1154. has_owner: false,
  1155. name: 'A title',
  1156. has_assignment: true,
  1157. brand_info: null
  1158. })
  1159. return this.TokenAccessController.readOnlyToken(
  1160. this.req,
  1161. this.res,
  1162. this.next
  1163. )
  1164. })
  1165. it('should render v2-import page', function(done) {
  1166. expect(
  1167. this.res.render.calledWith('project/v2-import', {
  1168. projectId: 'abcd',
  1169. hasOwner: false,
  1170. name: 'A title',
  1171. hasAssignment: true,
  1172. brandInfo: null
  1173. })
  1174. ).to.equal(true)
  1175. return done()
  1176. })
  1177. })
  1178. describe('with brand info', function() {
  1179. beforeEach(function() {
  1180. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1181. exists: true,
  1182. exported: false,
  1183. has_owner: false,
  1184. name: 'A title',
  1185. has_assignment: false,
  1186. brand_info: 'f1000'
  1187. })
  1188. return this.TokenAccessController.readOnlyToken(
  1189. this.req,
  1190. this.res,
  1191. this.next
  1192. )
  1193. })
  1194. it('should render v2-import page', function(done) {
  1195. expect(
  1196. this.res.render.calledWith('project/v2-import', {
  1197. projectId: 'abcd',
  1198. hasOwner: false,
  1199. name: 'A title',
  1200. hasAssignment: false,
  1201. brandInfo: 'f1000'
  1202. })
  1203. ).to.equal(true)
  1204. return done()
  1205. })
  1206. })
  1207. })
  1208. describe('when project was exported from v1', function() {
  1209. beforeEach(function() {
  1210. this.req = new MockRequest()
  1211. this.res = new MockResponse()
  1212. this.res.redirect = sinon.stub()
  1213. this.next = sinon.stub()
  1214. this.req.params['read_only_token'] = 'abcd'
  1215. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1216. .stub()
  1217. .callsArgWith(1, null, null, false)
  1218. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1219. allow: true,
  1220. exists: true,
  1221. exported: true
  1222. })
  1223. return this.TokenAccessController.readOnlyToken(
  1224. this.req,
  1225. this.res,
  1226. this.next
  1227. )
  1228. })
  1229. it('should call next with a not-found error', function(done) {
  1230. expect(this.next.callCount).to.equal(1)
  1231. return done()
  1232. })
  1233. })
  1234. describe('when token access is off, but user has higher access anyway', function() {
  1235. beforeEach(function() {
  1236. this.req = new MockRequest()
  1237. this.res = new MockResponse()
  1238. this.res.redirect = sinon.stub()
  1239. this.next = sinon.stub()
  1240. this.req.params['read_and_write_token'] = this.readAndWriteToken
  1241. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  1242. .stub()
  1243. .callsArgWith(1, null, null, true)
  1244. this.TokenAccessHandler.findProjectWithHigherAccess = sinon
  1245. .stub()
  1246. .callsArgWith(2, null, this.project)
  1247. this.TokenAccessHandler.addReadAndWriteUserToProject = sinon
  1248. .stub()
  1249. .callsArgWith(2, null)
  1250. this.ProjectController.loadEditor = sinon.stub()
  1251. return this.TokenAccessController.readAndWriteToken(
  1252. this.req,
  1253. this.res,
  1254. this.next
  1255. )
  1256. })
  1257. it('should try to find a project with this token', function(done) {
  1258. expect(
  1259. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  1260. ).to.equal(1)
  1261. expect(
  1262. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  1263. this.readAndWriteToken
  1264. )
  1265. ).to.equal(true)
  1266. return done()
  1267. })
  1268. it('should check if user has higher access to the token project', function(done) {
  1269. expect(
  1270. this.TokenAccessHandler.findProjectWithHigherAccess.callCount
  1271. ).to.equal(1)
  1272. return done()
  1273. })
  1274. it('should not add the user to the project with read-write access', function(done) {
  1275. expect(
  1276. this.TokenAccessHandler.addReadAndWriteUserToProject.callCount
  1277. ).to.equal(0)
  1278. return done()
  1279. })
  1280. it('should not pass control to loadEditor', function(done) {
  1281. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  1282. expect(
  1283. this.ProjectController.loadEditor.calledWith(
  1284. this.req,
  1285. this.res,
  1286. this.next
  1287. )
  1288. ).to.equal(false)
  1289. return done()
  1290. })
  1291. it('should not call next with a not-found error', function(done) {
  1292. expect(this.next.callCount).to.equal(0)
  1293. return done()
  1294. })
  1295. it('should redirect to the canonical project url', function(done) {
  1296. expect(this.res.redirect.callCount).to.equal(1)
  1297. expect(
  1298. this.res.redirect.calledWith(302, `/project/${this.project._id}`)
  1299. ).to.equal(true)
  1300. return done()
  1301. })
  1302. })
  1303. describe('when higher access is not available', function() {
  1304. beforeEach(function() {
  1305. this.req = new MockRequest()
  1306. this.res = new MockResponse()
  1307. this.next = sinon.stub()
  1308. this.req.params['read_and_write_token'] = this.readAndWriteToken
  1309. this.TokenAccessHandler.findProjectWithReadAndWriteToken = sinon
  1310. .stub()
  1311. .callsArgWith(1, null, null, true)
  1312. this.TokenAccessHandler.findProjectWithHigherAccess = sinon
  1313. .stub()
  1314. .callsArgWith(2, null, null)
  1315. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  1316. .stub()
  1317. .callsArgWith(2, null)
  1318. this.ProjectController.loadEditor = sinon.stub()
  1319. return this.TokenAccessController.readAndWriteToken(
  1320. this.req,
  1321. this.res,
  1322. this.next
  1323. )
  1324. })
  1325. it('should try to find a project with this token', function(done) {
  1326. expect(
  1327. this.TokenAccessHandler.findProjectWithReadAndWriteToken.callCount
  1328. ).to.equal(1)
  1329. expect(
  1330. this.TokenAccessHandler.findProjectWithReadAndWriteToken.calledWith(
  1331. this.readAndWriteToken
  1332. )
  1333. ).to.equal(true)
  1334. return done()
  1335. })
  1336. it('should check if user has higher access to the token project', function(done) {
  1337. expect(
  1338. this.TokenAccessHandler.findProjectWithHigherAccess.callCount
  1339. ).to.equal(1)
  1340. return done()
  1341. })
  1342. it('should not add the user to the project with read-write access', function(done) {
  1343. expect(
  1344. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  1345. ).to.equal(0)
  1346. return done()
  1347. })
  1348. it('should not pass control to loadEditor', function(done) {
  1349. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  1350. expect(
  1351. this.ProjectController.loadEditor.calledWith(
  1352. this.req,
  1353. this.res,
  1354. this.next
  1355. )
  1356. ).to.equal(false)
  1357. return done()
  1358. })
  1359. it('should call next with a not-found error', function(done) {
  1360. expect(this.next.callCount).to.equal(1)
  1361. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  1362. return done()
  1363. })
  1364. })
  1365. })
  1366. describe('when adding user to project produces an error', function() {
  1367. beforeEach(function() {
  1368. this.req = new MockRequest()
  1369. this.res = new MockResponse()
  1370. this.next = sinon.stub()
  1371. this.req.params['read_only_token'] = this.readOnlyToken
  1372. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1373. .stub()
  1374. .callsArgWith(1, null, this.project, true)
  1375. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  1376. .stub()
  1377. .callsArgWith(2, new Error('woops'))
  1378. this.ProjectController.loadEditor = sinon.stub()
  1379. return this.TokenAccessController.readOnlyToken(
  1380. this.req,
  1381. this.res,
  1382. this.next
  1383. )
  1384. })
  1385. it('should try to find a project with this token', function(done) {
  1386. expect(
  1387. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  1388. ).to.equal(1)
  1389. expect(
  1390. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  1391. this.readOnlyToken
  1392. )
  1393. ).to.equal(true)
  1394. return done()
  1395. })
  1396. it('should add the user to the project with read-only access', function(done) {
  1397. expect(
  1398. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  1399. ).to.equal(1)
  1400. expect(
  1401. this.TokenAccessHandler.addReadOnlyUserToProject.calledWith(
  1402. this.userId.toString(),
  1403. this.projectId
  1404. )
  1405. ).to.equal(true)
  1406. return done()
  1407. })
  1408. it('should not pass control to loadEditor', function(done) {
  1409. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  1410. expect(
  1411. this.ProjectController.loadEditor.calledWith(
  1412. this.req,
  1413. this.res,
  1414. this.next
  1415. )
  1416. ).to.equal(false)
  1417. return done()
  1418. })
  1419. it('should call next with an error', function(done) {
  1420. expect(this.next.callCount).to.equal(1)
  1421. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  1422. return done()
  1423. })
  1424. })
  1425. describe('anonymous', function() {
  1426. beforeEach(function() {
  1427. this.AuthenticationController.getLoggedInUserId = sinon
  1428. .stub()
  1429. .returns(null)
  1430. return (this.TokenAccessHandler.grantSessionTokenAccess = sinon.stub())
  1431. })
  1432. describe('when all goes well', function() {
  1433. beforeEach(function() {
  1434. this.req = new MockRequest()
  1435. this.res = new MockResponse()
  1436. this.next = sinon.stub()
  1437. this.req.params['read_only_token'] = this.readOnlyToken
  1438. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1439. .stub()
  1440. .callsArgWith(1, null, this.project, true)
  1441. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  1442. .stub()
  1443. .callsArgWith(2, null)
  1444. this.ProjectController.loadEditor = sinon.stub()
  1445. return this.TokenAccessController.readOnlyToken(
  1446. this.req,
  1447. this.res,
  1448. this.next
  1449. )
  1450. })
  1451. it('should try to find a project with this token', function(done) {
  1452. expect(
  1453. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  1454. ).to.equal(1)
  1455. expect(
  1456. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  1457. this.readOnlyToken
  1458. )
  1459. ).to.equal(true)
  1460. return done()
  1461. })
  1462. it('should give the user session read-only access', function(done) {
  1463. expect(
  1464. this.TokenAccessHandler.grantSessionTokenAccess.callCount
  1465. ).to.equal(1)
  1466. expect(
  1467. this.TokenAccessHandler.grantSessionTokenAccess.calledWith(
  1468. this.req,
  1469. this.projectId,
  1470. this.readOnlyToken
  1471. )
  1472. ).to.equal(true)
  1473. return done()
  1474. })
  1475. it('should not add the user to the project with read-only access', function(done) {
  1476. expect(
  1477. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  1478. ).to.equal(0)
  1479. return done()
  1480. })
  1481. it('should pass control to loadEditor', function(done) {
  1482. expect(this.req.params.Project_id).to.equal(this.projectId.toString())
  1483. expect(this.req._anonymousAccessToken).to.equal(this.readOnlyToken)
  1484. expect(this.ProjectController.loadEditor.callCount).to.equal(1)
  1485. expect(
  1486. this.ProjectController.loadEditor.calledWith(
  1487. this.req,
  1488. this.res,
  1489. this.next
  1490. )
  1491. ).to.equal(true)
  1492. return done()
  1493. })
  1494. })
  1495. describe('when findProject produces an error', function() {
  1496. beforeEach(function() {
  1497. this.req = new MockRequest()
  1498. this.res = new MockResponse()
  1499. this.next = sinon.stub()
  1500. this.req.params['read_only_token'] = this.readOnlyToken
  1501. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1502. .stub()
  1503. .callsArgWith(1, new Error('woops'))
  1504. this.TokenAccessHandler.addReadOnlyUserToProject = sinon
  1505. .stub()
  1506. .callsArgWith(2, null)
  1507. this.ProjectController.loadEditor = sinon.stub()
  1508. return this.TokenAccessController.readOnlyToken(
  1509. this.req,
  1510. this.res,
  1511. this.next
  1512. )
  1513. })
  1514. it('should try to find a project with this token', function(done) {
  1515. expect(
  1516. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  1517. ).to.equal(1)
  1518. expect(
  1519. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  1520. this.readOnlyToken
  1521. )
  1522. ).to.equal(true)
  1523. return done()
  1524. })
  1525. it('should not give the user session read-only access', function(done) {
  1526. expect(
  1527. this.TokenAccessHandler.grantSessionTokenAccess.callCount
  1528. ).to.equal(0)
  1529. return done()
  1530. })
  1531. it('should not add the user to the project with read-only access', function(done) {
  1532. expect(
  1533. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  1534. ).to.equal(0)
  1535. return done()
  1536. })
  1537. it('should not pass control to loadEditor', function(done) {
  1538. expect(this.ProjectController.loadEditor.callCount).to.equal(0)
  1539. expect(
  1540. this.ProjectController.loadEditor.calledWith(
  1541. this.req,
  1542. this.res,
  1543. this.next
  1544. )
  1545. ).to.equal(false)
  1546. return done()
  1547. })
  1548. it('should call next with an error', function(done) {
  1549. expect(this.next.callCount).to.equal(1)
  1550. expect(this.next.lastCall.args[0]).to.be.instanceof(Error)
  1551. return done()
  1552. })
  1553. })
  1554. describe('when findProject does not find a project', function() {
  1555. beforeEach(function() {
  1556. this.req = new MockRequest()
  1557. this.res = new MockResponse()
  1558. this.res.redirect = sinon.stub()
  1559. this.next = sinon.stub()
  1560. this.req.params['read_only_token'] = this.readOnlyToken
  1561. this.AuthenticationController.getLoggedInUserId = sinon
  1562. .stub()
  1563. .returns(this.userId.toString())
  1564. this.TokenAccessHandler.findProjectWithReadOnlyToken = sinon
  1565. .stub()
  1566. .callsArgWith(1, null, false)
  1567. return (this.TokenAccessHandler.addReadOnlyUserToProject = sinon.stub())
  1568. })
  1569. describe('when project does not exist', function() {
  1570. beforeEach(function() {
  1571. return this.TokenAccessController.readOnlyToken(
  1572. this.req,
  1573. this.res,
  1574. this.next
  1575. )
  1576. })
  1577. it('should try to find a project with this token', function(done) {
  1578. expect(
  1579. this.TokenAccessHandler.findProjectWithReadOnlyToken.callCount
  1580. ).to.equal(1)
  1581. expect(
  1582. this.TokenAccessHandler.findProjectWithReadOnlyToken.calledWith(
  1583. this.readOnlyToken
  1584. )
  1585. ).to.equal(true)
  1586. return done()
  1587. })
  1588. it('should not give the user session read-only access', function(done) {
  1589. expect(
  1590. this.TokenAccessHandler.grantSessionTokenAccess.callCount
  1591. ).to.equal(0)
  1592. return done()
  1593. })
  1594. it('should not add the user to the project with read-only access', function(done) {
  1595. expect(
  1596. this.TokenAccessHandler.addReadOnlyUserToProject.callCount
  1597. ).to.equal(0)
  1598. return done()
  1599. })
  1600. })
  1601. describe('when project was exported to v2', function() {
  1602. beforeEach(function() {
  1603. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1604. exists: true,
  1605. exported: true
  1606. })
  1607. return this.TokenAccessController.readOnlyToken(
  1608. this.req,
  1609. this.res,
  1610. this.next
  1611. )
  1612. })
  1613. it('should call next with not found error', function(done) {
  1614. expect(this.next.callCount).to.equal(1)
  1615. expect(
  1616. this.next.calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  1617. ).to.equal(true)
  1618. return done()
  1619. })
  1620. })
  1621. describe('when project was not exported to v2', function() {
  1622. beforeEach(function() {
  1623. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1624. exists: true,
  1625. exported: false
  1626. })
  1627. this.res.render = sinon.stub()
  1628. this.TokenAccessController.readOnlyToken(
  1629. this.req,
  1630. this.res,
  1631. this.next
  1632. )
  1633. })
  1634. it('should not redirect to v1', function(done) {
  1635. expect(this.res.redirect.callCount).to.equal(0)
  1636. done()
  1637. })
  1638. it('should show project import page', function(done) {
  1639. expect(this.res.render.calledWith('project/v2-import')).to.equal(
  1640. true
  1641. )
  1642. done()
  1643. })
  1644. })
  1645. describe('when project does not exist on v1', function() {
  1646. beforeEach(function() {
  1647. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1648. exists: false,
  1649. exported: false
  1650. })
  1651. return this.TokenAccessController.readOnlyToken(
  1652. this.req,
  1653. this.res,
  1654. this.next
  1655. )
  1656. })
  1657. it('should call next with not found error', function(done) {
  1658. expect(this.next.callCount).to.equal(1)
  1659. expect(
  1660. this.next.calledWith(sinon.match.instanceOf(Errors.NotFoundError))
  1661. ).to.equal(true)
  1662. return done()
  1663. })
  1664. })
  1665. describe('anonymous user', function() {
  1666. beforeEach(function() {
  1667. return (this.AuthenticationController.getLoggedInUserId = sinon
  1668. .stub()
  1669. .returns(null))
  1670. })
  1671. describe('when project was not exported to v2', function() {
  1672. beforeEach(function() {
  1673. this.TokenAccessHandler.getV1DocInfo = sinon.stub().yields(null, {
  1674. exists: true,
  1675. exported: false
  1676. })
  1677. this.res.render = sinon.stub()
  1678. this.TokenAccessController.readOnlyToken(
  1679. this.req,
  1680. this.res,
  1681. this.next
  1682. )
  1683. })
  1684. it('should not redirect to v1', function(done) {
  1685. expect(this.res.redirect.callCount).to.equal(0)
  1686. done()
  1687. })
  1688. it('should show project import page', function(done) {
  1689. expect(this.res.render.calledWith('project/v2-import')).to.equal(
  1690. true
  1691. )
  1692. done()
  1693. })
  1694. })
  1695. describe('force-import-to-v2 flag is on', function() {
  1696. beforeEach(function() {
  1697. this.res.render = sinon.stub()
  1698. return this.Features.hasFeature.returns(true)
  1699. })
  1700. describe('when project was not exported to v2', function() {
  1701. beforeEach(function() {
  1702. this.TokenAccessHandler.getV1DocInfo = sinon
  1703. .stub()
  1704. .yields(null, {
  1705. exists: true,
  1706. exported: false
  1707. })
  1708. return this.TokenAccessController.readOnlyToken(
  1709. this.req,
  1710. this.res,
  1711. this.next
  1712. )
  1713. })
  1714. it('should render anonymous import status page', function(done) {
  1715. expect(this.res.render.callCount).to.equal(1)
  1716. expect(
  1717. this.res.render.calledWith('project/v2-import', {
  1718. loginRedirect: `/read/${this.readOnlyToken}`
  1719. })
  1720. ).to.equal(true)
  1721. return done()
  1722. })
  1723. })
  1724. })
  1725. })
  1726. })
  1727. })
  1728. })
  1729. })