TokenAccessTests.js 41 KB

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