TokenAccessTests.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272
  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 tryContentAccess = (user, projcetId, test, callback) => {
  95. // The real-time service calls this end point to determine the user's
  96. // permissions.
  97. let userId
  98. if (user.id != null) {
  99. userId = user.id
  100. } else {
  101. userId = 'anonymous-user'
  102. }
  103. request.post(
  104. {
  105. url: `/project/${projcetId}/join`,
  106. qs: { user_id: userId },
  107. auth: {
  108. user: settings.apis.web.user,
  109. pass: settings.apis.web.pass,
  110. sendImmediately: true,
  111. },
  112. json: true,
  113. jar: false,
  114. },
  115. (error, response, body) => {
  116. if (error != null) {
  117. return callback(error)
  118. }
  119. test(response, body)
  120. callback()
  121. }
  122. )
  123. }
  124. const tryAnonContentAccess = (user, projectId, token, test, callback) => {
  125. // The real-time service calls this end point to determine the user's
  126. // permissions.
  127. let userId
  128. if (user.id != null) {
  129. userId = user.id
  130. } else {
  131. userId = 'anonymous-user'
  132. }
  133. request.post(
  134. {
  135. url: `/project/${projectId}/join`,
  136. qs: { user_id: userId },
  137. auth: {
  138. user: settings.apis.web.user,
  139. pass: settings.apis.web.pass,
  140. sendImmediately: true,
  141. },
  142. headers: {
  143. 'x-sl-anonymous-access-token': token,
  144. },
  145. json: true,
  146. jar: false,
  147. },
  148. (error, response, body) => {
  149. if (error != null) {
  150. return callback(error)
  151. }
  152. test(response, body)
  153. callback()
  154. }
  155. )
  156. }
  157. describe('TokenAccess', function () {
  158. beforeEach(function (done) {
  159. this.timeout(90000)
  160. this.owner = new User()
  161. this.other1 = new User()
  162. this.other2 = new User()
  163. this.anon = new User()
  164. this.siteAdmin = new User({ email: 'admin@example.com' })
  165. async.parallel(
  166. [
  167. cb =>
  168. this.siteAdmin.login(err => {
  169. if (err) {
  170. return cb(err)
  171. }
  172. this.siteAdmin.ensureAdmin(cb)
  173. }),
  174. cb => this.owner.login(cb),
  175. cb => this.other1.login(cb),
  176. cb => this.other2.login(cb),
  177. cb => this.anon.getCsrfToken(cb),
  178. ],
  179. done
  180. )
  181. })
  182. describe('no token-access', function () {
  183. beforeEach(function (done) {
  184. this.owner.createProject(
  185. `token-ro-test${Math.random()}`,
  186. (err, projectId) => {
  187. if (err != null) {
  188. return done(err)
  189. }
  190. this.projectId = projectId
  191. // Note, never made token-based,
  192. // thus no tokens
  193. done()
  194. }
  195. )
  196. })
  197. it('should deny access ', function (done) {
  198. async.series(
  199. [
  200. cb => {
  201. tryEditorAccess(
  202. this.other1,
  203. this.projectId,
  204. expectErrorResponse.restricted.html,
  205. cb
  206. )
  207. },
  208. cb => {
  209. tryContentAccess(
  210. this.other1,
  211. this.projectId,
  212. (response, body) => {
  213. expect(response.statusCode).to.equal(403)
  214. expect(body).to.equal('Forbidden')
  215. },
  216. cb
  217. )
  218. },
  219. ],
  220. done
  221. )
  222. })
  223. })
  224. describe('read-only token', function () {
  225. beforeEach(function (done) {
  226. this.owner.createProject(
  227. `token-ro-test${Math.random()}`,
  228. (err, projectId) => {
  229. if (err != null) {
  230. return done(err)
  231. }
  232. this.projectId = projectId
  233. this.owner.makeTokenBased(this.projectId, err => {
  234. if (err != null) {
  235. return done(err)
  236. }
  237. this.owner.getProject(this.projectId, (err, project) => {
  238. if (err != null) {
  239. return done(err)
  240. }
  241. this.tokens = project.tokens
  242. done()
  243. })
  244. })
  245. }
  246. )
  247. })
  248. it('allow the user read-only access to the project', function (done) {
  249. async.series(
  250. [
  251. cb => {
  252. // deny access before token is used
  253. tryEditorAccess(
  254. this.other1,
  255. this.projectId,
  256. expectErrorResponse.restricted.html,
  257. cb
  258. )
  259. },
  260. cb => {
  261. // use token
  262. tryReadOnlyTokenAccess(
  263. this.other1,
  264. this.tokens.readOnly,
  265. (response, body) => {
  266. expect(response.statusCode).to.equal(200)
  267. },
  268. (response, body) => {
  269. expect(response.statusCode).to.equal(200)
  270. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  271. expect(body.tokenAccessGranted).to.equal('readOnly')
  272. },
  273. cb
  274. )
  275. },
  276. cb => {
  277. // allow content access read-only
  278. tryContentAccess(
  279. this.other1,
  280. this.projectId,
  281. (response, body) => {
  282. expect(body.privilegeLevel).to.equal('readOnly')
  283. expect(body.isRestrictedUser).to.equal(true)
  284. expect(body.project.owner).to.have.keys('_id')
  285. expect(body.project.owner).to.not.have.any.keys(
  286. 'email',
  287. 'first_name',
  288. 'last_name'
  289. )
  290. },
  291. cb
  292. )
  293. },
  294. cb => {
  295. tryEditorAccess(
  296. this.other1,
  297. this.projectId,
  298. (response, body) => {
  299. expect(response.statusCode).to.equal(200)
  300. },
  301. cb
  302. )
  303. },
  304. ],
  305. done
  306. )
  307. })
  308. it('should redirect the admin to the project (with rw access)', function (done) {
  309. async.series(
  310. [
  311. cb => {
  312. // use token
  313. tryReadOnlyTokenAccess(
  314. this.siteAdmin,
  315. this.tokens.readOnly,
  316. (response, body) => {
  317. expect(response.statusCode).to.equal(200)
  318. },
  319. (response, body) => {
  320. expect(response.statusCode).to.equal(200)
  321. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  322. },
  323. cb
  324. )
  325. },
  326. cb => {
  327. // allow content access read-and-write
  328. tryContentAccess(
  329. this.siteAdmin,
  330. this.projectId,
  331. (response, body) => {
  332. expect(body.privilegeLevel).to.equal('owner')
  333. expect(body.isRestrictedUser).to.equal(false)
  334. },
  335. cb
  336. )
  337. },
  338. ],
  339. done
  340. )
  341. })
  342. describe('made private again', function () {
  343. beforeEach(function (done) {
  344. this.owner.makePrivate(this.projectId, () => setTimeout(done, 1000))
  345. })
  346. it('should not allow the user to access the project', function (done) {
  347. async.series(
  348. [
  349. // no access before token is used
  350. cb =>
  351. tryEditorAccess(
  352. this.other1,
  353. this.projectId,
  354. expectErrorResponse.restricted.html,
  355. cb
  356. ),
  357. // token goes nowhere
  358. cb =>
  359. tryReadOnlyTokenAccess(
  360. this.other1,
  361. this.tokens.readOnly,
  362. (response, body) => {
  363. expect(response.statusCode).to.equal(200)
  364. },
  365. (response, body) => {
  366. expect(response.statusCode).to.equal(404)
  367. },
  368. cb
  369. ),
  370. // still no access
  371. cb =>
  372. tryEditorAccess(
  373. this.other1,
  374. this.projectId,
  375. expectErrorResponse.restricted.html,
  376. cb
  377. ),
  378. cb =>
  379. tryContentAccess(
  380. this.other1,
  381. this.projectId,
  382. (response, body) => {
  383. expect(response.statusCode).to.equal(403)
  384. expect(body).to.equal('Forbidden')
  385. },
  386. cb
  387. ),
  388. ],
  389. done
  390. )
  391. })
  392. })
  393. })
  394. describe('anonymous read-only token', function () {
  395. beforeEach(function (done) {
  396. this.owner.createProject(
  397. `token-anon-ro-test${Math.random()}`,
  398. (err, projectId) => {
  399. if (err != null) {
  400. return done(err)
  401. }
  402. this.projectId = projectId
  403. this.owner.makeTokenBased(this.projectId, err => {
  404. if (err != null) {
  405. return done(err)
  406. }
  407. this.owner.getProject(this.projectId, (err, project) => {
  408. if (err != null) {
  409. return done(err)
  410. }
  411. this.tokens = project.tokens
  412. done()
  413. })
  414. })
  415. }
  416. )
  417. })
  418. it('should allow the user to access project via read-only token url', function (done) {
  419. async.series(
  420. [
  421. cb =>
  422. tryEditorAccess(
  423. this.anon,
  424. this.projectId,
  425. expectErrorResponse.restricted.html,
  426. cb
  427. ),
  428. cb =>
  429. tryReadOnlyTokenAccess(
  430. this.anon,
  431. this.tokens.readOnly,
  432. (response, body) => {
  433. expect(response.statusCode).to.equal(200)
  434. },
  435. (response, body) => {
  436. expect(response.statusCode).to.equal(200)
  437. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  438. expect(body.grantAnonymousAccess).to.equal('readOnly')
  439. },
  440. cb
  441. ),
  442. cb =>
  443. tryEditorAccess(
  444. this.anon,
  445. this.projectId,
  446. (response, body) => {
  447. expect(response.statusCode).to.equal(200)
  448. },
  449. cb
  450. ),
  451. cb =>
  452. tryAnonContentAccess(
  453. this.anon,
  454. this.projectId,
  455. this.tokens.readOnly,
  456. (response, body) => {
  457. expect(body.privilegeLevel).to.equal('readOnly')
  458. expect(body.isRestrictedUser).to.equal(true)
  459. expect(body.project.owner).to.have.keys('_id')
  460. expect(body.project.owner).to.not.have.any.keys(
  461. 'email',
  462. 'first_name',
  463. 'last_name'
  464. )
  465. },
  466. cb
  467. ),
  468. ],
  469. done
  470. )
  471. })
  472. describe('made private again', function () {
  473. beforeEach(function (done) {
  474. this.owner.makePrivate(this.projectId, () => setTimeout(done, 1000))
  475. })
  476. it('should deny access to project', function (done) {
  477. async.series(
  478. [
  479. cb =>
  480. tryEditorAccess(
  481. this.anon,
  482. this.projectId,
  483. expectErrorResponse.restricted.html,
  484. cb
  485. ),
  486. // should not allow the user to access read-only token
  487. cb =>
  488. tryReadOnlyTokenAccess(
  489. this.anon,
  490. this.tokens.readOnly,
  491. (response, body) => {
  492. expect(response.statusCode).to.equal(200)
  493. },
  494. (response, body) => {
  495. expect(response.statusCode).to.equal(404)
  496. },
  497. cb
  498. ),
  499. // still no access
  500. cb =>
  501. tryEditorAccess(
  502. this.anon,
  503. this.projectId,
  504. expectErrorResponse.restricted.html,
  505. cb
  506. ),
  507. // should not allow the user to join the project
  508. cb =>
  509. tryAnonContentAccess(
  510. this.anon,
  511. this.projectId,
  512. this.tokens.readOnly,
  513. (response, body) => {
  514. expect(response.statusCode).to.equal(403)
  515. expect(body).to.equal('Forbidden')
  516. },
  517. cb
  518. ),
  519. ],
  520. done
  521. )
  522. })
  523. })
  524. })
  525. describe('read-and-write token', function () {
  526. beforeEach(function (done) {
  527. this.owner.createProject(
  528. `token-rw-test${Math.random()}`,
  529. (err, projectId) => {
  530. if (err != null) {
  531. return done(err)
  532. }
  533. this.projectId = projectId
  534. this.owner.makeTokenBased(this.projectId, err => {
  535. if (err != null) {
  536. return done(err)
  537. }
  538. this.owner.getProject(this.projectId, (err, project) => {
  539. if (err != null) {
  540. return done(err)
  541. }
  542. this.tokens = project.tokens
  543. done()
  544. })
  545. })
  546. }
  547. )
  548. })
  549. it('should allow the user to access project via read-and-write token url', function (done) {
  550. async.series(
  551. [
  552. // deny access before the token is used
  553. cb =>
  554. tryEditorAccess(
  555. this.other1,
  556. this.projectId,
  557. expectErrorResponse.restricted.html,
  558. cb
  559. ),
  560. cb =>
  561. tryReadAndWriteTokenAccess(
  562. this.other1,
  563. this.tokens.readAndWrite,
  564. (response, body) => {
  565. expect(response.statusCode).to.equal(200)
  566. },
  567. (response, body) => {
  568. expect(response.statusCode).to.equal(200)
  569. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  570. expect(body.tokenAccessGranted).to.equal('readAndWrite')
  571. },
  572. cb
  573. ),
  574. cb =>
  575. tryEditorAccess(
  576. this.other1,
  577. this.projectId,
  578. (response, body) => {
  579. expect(response.statusCode).to.equal(200)
  580. },
  581. cb
  582. ),
  583. cb =>
  584. tryContentAccess(
  585. this.other1,
  586. this.projectId,
  587. (response, body) => {
  588. expect(body.privilegeLevel).to.equal('readAndWrite')
  589. expect(body.isRestrictedUser).to.equal(false)
  590. expect(body.project.owner).to.have.all.keys(
  591. '_id',
  592. 'email',
  593. 'first_name',
  594. 'last_name',
  595. 'privileges',
  596. 'signUpDate'
  597. )
  598. },
  599. cb
  600. ),
  601. ],
  602. done
  603. )
  604. })
  605. describe('upgrading from a read-only token', function () {
  606. beforeEach(function (done) {
  607. this.owner.createProject(
  608. `token-rw-upgrade-test${Math.random()}`,
  609. (err, projectId) => {
  610. if (err != null) {
  611. return done(err)
  612. }
  613. this.projectId = projectId
  614. this.owner.makeTokenBased(this.projectId, err => {
  615. if (err != null) {
  616. return done(err)
  617. }
  618. this.owner.getProject(this.projectId, (err, project) => {
  619. if (err != null) {
  620. return done(err)
  621. }
  622. this.tokens = project.tokens
  623. done()
  624. })
  625. })
  626. }
  627. )
  628. })
  629. it('should allow user to access project via read-only, then upgrade to read-write', function (done) {
  630. async.series(
  631. [
  632. // deny access before the token is used
  633. cb =>
  634. tryEditorAccess(
  635. this.other1,
  636. this.projectId,
  637. expectErrorResponse.restricted.html,
  638. cb
  639. ),
  640. cb => {
  641. // use read-only token
  642. tryReadOnlyTokenAccess(
  643. this.other1,
  644. this.tokens.readOnly,
  645. (response, body) => {
  646. expect(response.statusCode).to.equal(200)
  647. },
  648. (response, body) => {
  649. expect(response.statusCode).to.equal(200)
  650. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  651. expect(body.tokenAccessGranted).to.equal('readOnly')
  652. },
  653. cb
  654. )
  655. },
  656. cb => {
  657. tryEditorAccess(
  658. this.other1,
  659. this.projectId,
  660. (response, body) => {
  661. expect(response.statusCode).to.equal(200)
  662. },
  663. cb
  664. )
  665. },
  666. cb => {
  667. // allow content access read-only
  668. tryContentAccess(
  669. this.other1,
  670. this.projectId,
  671. (response, body) => {
  672. expect(body.privilegeLevel).to.equal('readOnly')
  673. expect(body.isRestrictedUser).to.equal(true)
  674. expect(body.project.owner).to.have.keys('_id')
  675. expect(body.project.owner).to.not.have.any.keys(
  676. 'email',
  677. 'first_name',
  678. 'last_name'
  679. )
  680. },
  681. cb
  682. )
  683. },
  684. //
  685. // Then switch to read-write token
  686. //
  687. cb =>
  688. tryReadAndWriteTokenAccess(
  689. this.other1,
  690. this.tokens.readAndWrite,
  691. (response, body) => {
  692. expect(response.statusCode).to.equal(200)
  693. },
  694. (response, body) => {
  695. expect(response.statusCode).to.equal(200)
  696. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  697. expect(body.tokenAccessGranted).to.equal('readAndWrite')
  698. },
  699. cb
  700. ),
  701. cb =>
  702. tryEditorAccess(
  703. this.other1,
  704. this.projectId,
  705. (response, body) => {
  706. expect(response.statusCode).to.equal(200)
  707. },
  708. cb
  709. ),
  710. cb =>
  711. tryContentAccess(
  712. this.other1,
  713. this.projectId,
  714. (response, body) => {
  715. expect(body.privilegeLevel).to.equal('readAndWrite')
  716. expect(body.isRestrictedUser).to.equal(false)
  717. expect(body.project.owner).to.have.all.keys(
  718. '_id',
  719. 'email',
  720. 'first_name',
  721. 'last_name',
  722. 'privileges',
  723. 'signUpDate'
  724. )
  725. },
  726. cb
  727. ),
  728. ],
  729. done
  730. )
  731. })
  732. })
  733. describe('made private again', function () {
  734. beforeEach(function (done) {
  735. this.owner.makePrivate(this.projectId, () => setTimeout(done, 1000))
  736. })
  737. it('should deny access to project', function (done) {
  738. async.series(
  739. [
  740. cb => {
  741. tryEditorAccess(
  742. this.other1,
  743. this.projectId,
  744. (response, body) => {},
  745. cb
  746. )
  747. },
  748. cb => {
  749. tryReadAndWriteTokenAccess(
  750. this.other1,
  751. this.tokens.readAndWrite,
  752. (response, body) => {
  753. expect(response.statusCode).to.equal(200)
  754. },
  755. (response, body) => {
  756. expect(response.statusCode).to.equal(404)
  757. },
  758. cb
  759. )
  760. },
  761. cb => {
  762. tryEditorAccess(
  763. this.other1,
  764. this.projectId,
  765. expectErrorResponse.restricted.html,
  766. cb
  767. )
  768. },
  769. cb => {
  770. tryContentAccess(
  771. this.other1,
  772. this.projectId,
  773. (response, body) => {
  774. expect(response.statusCode).to.equal(403)
  775. expect(body).to.equal('Forbidden')
  776. },
  777. cb
  778. )
  779. },
  780. ],
  781. done
  782. )
  783. })
  784. })
  785. })
  786. if (!settings.allowAnonymousReadAndWriteSharing) {
  787. describe('anonymous read-and-write token, disabled', function () {
  788. beforeEach(function (done) {
  789. this.owner.createProject(
  790. `token-anon-rw-test${Math.random()}`,
  791. (err, projectId) => {
  792. if (err != null) {
  793. return done(err)
  794. }
  795. this.projectId = projectId
  796. this.owner.makeTokenBased(this.projectId, err => {
  797. if (err != null) {
  798. return done(err)
  799. }
  800. this.owner.getProject(this.projectId, (err, project) => {
  801. if (err != null) {
  802. return done(err)
  803. }
  804. this.tokens = project.tokens
  805. done()
  806. })
  807. })
  808. }
  809. )
  810. })
  811. it('should not allow the user to access read-and-write token', function (done) {
  812. async.series(
  813. [
  814. cb =>
  815. tryEditorAccess(
  816. this.anon,
  817. this.projectId,
  818. expectErrorResponse.restricted.html,
  819. cb
  820. ),
  821. cb =>
  822. tryReadAndWriteTokenAccess(
  823. this.anon,
  824. this.tokens.readAndWrite,
  825. (response, body) => {
  826. expect(response.statusCode).to.equal(200)
  827. },
  828. (response, body) => {
  829. expect(response.statusCode).to.equal(200)
  830. expect(body).to.deep.equal({
  831. redirect: '/restricted',
  832. anonWriteAccessDenied: true,
  833. })
  834. },
  835. cb
  836. ),
  837. cb =>
  838. tryAnonContentAccess(
  839. this.anon,
  840. this.projectId,
  841. this.tokens.readAndWrite,
  842. (response, body) => {
  843. expect(response.statusCode).to.equal(403)
  844. expect(body).to.equal('Forbidden')
  845. },
  846. cb
  847. ),
  848. cb =>
  849. this.anon.login((err, response, body) => {
  850. expect(err).to.not.exist
  851. expect(response.statusCode).to.equal(200)
  852. expect(body.redir).to.equal(`/${this.tokens.readAndWrite}`)
  853. cb()
  854. }),
  855. ],
  856. done
  857. )
  858. })
  859. })
  860. } else {
  861. describe('anonymous read-and-write token, enabled', function () {
  862. beforeEach(function (done) {
  863. this.owner.createProject(
  864. `token-anon-rw-test${Math.random()}`,
  865. (err, projectId) => {
  866. if (err != null) {
  867. return done(err)
  868. }
  869. this.projectId = projectId
  870. this.owner.makeTokenBased(this.projectId, err => {
  871. if (err != null) {
  872. return done(err)
  873. }
  874. this.owner.getProject(this.projectId, (err, project) => {
  875. if (err != null) {
  876. return done(err)
  877. }
  878. this.tokens = project.tokens
  879. done()
  880. })
  881. })
  882. }
  883. )
  884. })
  885. it('should allow the user to access project via read-and-write token url', function (done) {
  886. async.series(
  887. [
  888. cb =>
  889. tryEditorAccess(
  890. this.anon,
  891. this.projectId,
  892. expectErrorResponse.restricted.html,
  893. cb
  894. ),
  895. cb =>
  896. tryReadAndWriteTokenAccess(
  897. this.anon,
  898. this.tokens.readAndWrite,
  899. (response, body) => {
  900. expect(response.statusCode).to.equal(200)
  901. },
  902. (response, body) => {
  903. expect(response.statusCode).to.equal(200)
  904. expect(body.redirect).to.equal(`/project/${this.projectId}`)
  905. expect(body.grantAnonymousAccess).to.equal('readAndWrite')
  906. },
  907. cb
  908. ),
  909. cb =>
  910. tryEditorAccess(
  911. this.anon,
  912. this.projectId,
  913. (response, body) => {
  914. expect(response.statusCode).to.equal(200)
  915. },
  916. cb
  917. ),
  918. cb =>
  919. tryAnonContentAccess(
  920. this.anon,
  921. this.projectId,
  922. this.tokens.readAndWrite,
  923. (response, body) => {
  924. expect(body.privilegeLevel).to.equal('readAndWrite')
  925. },
  926. cb
  927. ),
  928. ],
  929. done
  930. )
  931. })
  932. describe('made private again', function () {
  933. beforeEach(function (done) {
  934. this.owner.makePrivate(this.projectId, () => setTimeout(done, 1000))
  935. })
  936. it('should not allow the user to access read-and-write token', function (done) {
  937. async.series(
  938. [
  939. cb =>
  940. tryEditorAccess(
  941. this.anon,
  942. this.projectId,
  943. expectErrorResponse.restricted.html,
  944. cb
  945. ),
  946. cb =>
  947. tryReadAndWriteTokenAccess(
  948. this.anon,
  949. this.tokens.readAndWrite,
  950. (response, body) => {
  951. expect(response.statusCode).to.equal(200)
  952. },
  953. (response, body) => {
  954. expect(response.statusCode).to.equal(404)
  955. },
  956. cb
  957. ),
  958. cb =>
  959. tryEditorAccess(
  960. this.anon,
  961. this.projectId,
  962. expectErrorResponse.restricted.html,
  963. cb
  964. ),
  965. cb =>
  966. tryAnonContentAccess(
  967. this.anon,
  968. this.projectId,
  969. this.tokens.readAndWrite,
  970. (response, body) => {
  971. expect(response.statusCode).to.equal(403)
  972. expect(body).to.equal('Forbidden')
  973. },
  974. cb
  975. ),
  976. ],
  977. done
  978. )
  979. })
  980. })
  981. })
  982. }
  983. describe('private overleaf project', function () {
  984. beforeEach(function (done) {
  985. this.owner.createProject('overleaf-import', (err, projectId) => {
  986. expect(err).not.to.exist
  987. this.projectId = projectId
  988. this.owner.makeTokenBased(this.projectId, err => {
  989. expect(err).not.to.exist
  990. this.owner.getProject(this.projectId, (err, project) => {
  991. expect(err).not.to.exist
  992. this.tokens = project.tokens
  993. this.owner.makePrivate(this.projectId, () => {
  994. db.projects.updateOne(
  995. { _id: project._id },
  996. {
  997. $set: {
  998. overleaf: { id: 1234 },
  999. },
  1000. },
  1001. err => {
  1002. expect(err).not.to.exist
  1003. done()
  1004. }
  1005. )
  1006. })
  1007. })
  1008. })
  1009. })
  1010. })
  1011. it('should only allow the owner access to the project', function (done) {
  1012. async.series(
  1013. [
  1014. // should redirect to canonical path, when owner uses read-write token
  1015. cb =>
  1016. tryReadAndWriteTokenAccess(
  1017. this.owner,
  1018. this.tokens.readAndWrite,
  1019. (response, body) => {
  1020. expect(response.statusCode).to.equal(200)
  1021. },
  1022. (response, body) => {
  1023. expect(response.statusCode).to.equal(200)
  1024. expect(response.body.redirect).to.equal(
  1025. `/project/${this.projectId}`
  1026. )
  1027. expect(response.body.higherAccess).to.equal(true)
  1028. },
  1029. cb
  1030. ),
  1031. cb =>
  1032. tryEditorAccess(
  1033. this.owner,
  1034. this.projectId,
  1035. (response, body) => {
  1036. expect(response.statusCode).to.equal(200)
  1037. },
  1038. cb
  1039. ),
  1040. cb =>
  1041. tryContentAccess(
  1042. this.owner,
  1043. this.projectId,
  1044. (response, body) => {
  1045. expect(body.privilegeLevel).to.equal('owner')
  1046. },
  1047. cb
  1048. ),
  1049. // non-owner should be denied access
  1050. cb =>
  1051. tryContentAccess(
  1052. this.other2,
  1053. this.projectId,
  1054. (response, body) => {
  1055. expect(response.statusCode).to.equal(403)
  1056. expect(body).to.equal('Forbidden')
  1057. },
  1058. cb
  1059. ),
  1060. ],
  1061. done
  1062. )
  1063. })
  1064. })
  1065. describe('private project, with higher access', function () {
  1066. beforeEach(function (done) {
  1067. this.owner.createProject(
  1068. `higher-access-test-${Math.random()}`,
  1069. (err, projectId) => {
  1070. expect(err).not.to.exist
  1071. this.projectId = projectId
  1072. this.owner.addUserToProject(
  1073. this.projectId,
  1074. this.other1,
  1075. 'readAndWrite',
  1076. err => {
  1077. expect(err).not.to.exist
  1078. this.owner.makeTokenBased(this.projectId, err => {
  1079. expect(err).not.to.exist
  1080. this.owner.getProject(this.projectId, (err, project) => {
  1081. expect(err).not.to.exist
  1082. this.tokens = project.tokens
  1083. this.owner.makePrivate(this.projectId, () => {
  1084. setTimeout(done, 1000)
  1085. })
  1086. })
  1087. })
  1088. }
  1089. )
  1090. }
  1091. )
  1092. })
  1093. it('should allow the user access to the project', function (done) {
  1094. async.series(
  1095. [
  1096. // should redirect to canonical path, when user uses read-write token
  1097. cb =>
  1098. tryReadAndWriteTokenAccess(
  1099. this.other1,
  1100. this.tokens.readAndWrite,
  1101. (response, body) => {
  1102. expect(response.statusCode).to.equal(200)
  1103. },
  1104. (response, body) => {
  1105. expect(response.statusCode).to.equal(200)
  1106. expect(response.body.redirect).to.equal(
  1107. `/project/${this.projectId}`
  1108. )
  1109. expect(response.body.higherAccess).to.equal(true)
  1110. },
  1111. cb
  1112. ),
  1113. // should redirect to canonical path, when user uses read-only token
  1114. cb =>
  1115. tryReadOnlyTokenAccess(
  1116. this.other1,
  1117. this.tokens.readOnly,
  1118. (response, body) => {
  1119. expect(response.statusCode).to.equal(200)
  1120. },
  1121. (response, body) => {
  1122. expect(response.statusCode).to.equal(200)
  1123. expect(response.body.redirect).to.equal(
  1124. `/project/${this.projectId}`
  1125. )
  1126. expect(response.body.higherAccess).to.equal(true)
  1127. },
  1128. cb
  1129. ),
  1130. // should allow the user access to the project
  1131. cb =>
  1132. tryEditorAccess(
  1133. this.other1,
  1134. this.projectId,
  1135. (response, body) => {
  1136. expect(response.statusCode).to.equal(200)
  1137. },
  1138. cb
  1139. ),
  1140. // should allow user to join the project
  1141. cb =>
  1142. tryContentAccess(
  1143. this.other1,
  1144. this.projectId,
  1145. (response, body) => {
  1146. expect(body.privilegeLevel).to.equal('readAndWrite')
  1147. },
  1148. cb
  1149. ),
  1150. // should not allow a different user to join the project
  1151. cb =>
  1152. tryEditorAccess(
  1153. this.other2,
  1154. this.projectId,
  1155. expectErrorResponse.restricted.html,
  1156. cb
  1157. ),
  1158. cb =>
  1159. tryContentAccess(
  1160. this.other2,
  1161. this.projectId,
  1162. (response, body) => {
  1163. expect(response.statusCode).to.equal(403)
  1164. expect(body).to.equal('Forbidden')
  1165. },
  1166. cb
  1167. ),
  1168. ],
  1169. done
  1170. )
  1171. })
  1172. })
  1173. describe('deleted project', function () {
  1174. beforeEach(function (done) {
  1175. this.owner.createProject(
  1176. `delete-test${Math.random()}`,
  1177. (err, projectId) => {
  1178. if (err != null) {
  1179. return done(err)
  1180. }
  1181. this.projectId = projectId
  1182. this.owner.makeTokenBased(this.projectId, err => {
  1183. if (err != null) {
  1184. return done(err)
  1185. }
  1186. this.owner.getProject(this.projectId, (err, project) => {
  1187. if (err != null) {
  1188. return done(err)
  1189. }
  1190. this.tokens = project.tokens
  1191. done()
  1192. })
  1193. })
  1194. }
  1195. )
  1196. })
  1197. it('should 404', function (done) {
  1198. async.series(
  1199. [
  1200. // delete project
  1201. cb => {
  1202. this.owner.deleteProject(this.projectId, cb)
  1203. },
  1204. cb => {
  1205. // use read-only token
  1206. tryReadOnlyTokenAccess(
  1207. this.other1,
  1208. this.tokens.readOnly,
  1209. (response, body) => {
  1210. expect(response.statusCode).to.equal(200)
  1211. },
  1212. (response, body) => {
  1213. expect(response.statusCode).to.equal(404)
  1214. },
  1215. cb
  1216. )
  1217. },
  1218. cb => {
  1219. // use read-write token
  1220. tryReadAndWriteTokenAccess(
  1221. this.other1,
  1222. this.tokens.readAndWrite,
  1223. (response, body) => {
  1224. expect(response.statusCode).to.equal(200)
  1225. },
  1226. (response, body) => {
  1227. expect(response.statusCode).to.equal(404)
  1228. },
  1229. cb
  1230. )
  1231. },
  1232. ],
  1233. done
  1234. )
  1235. })
  1236. })
  1237. })