TokenAccessTests.js 51 KB

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