AuthorizationTests.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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 expectErrorResponse = require('./helpers/expectErrorResponse')
  7. function tryReadAccess(user, projectId, test, callback) {
  8. async.series(
  9. [
  10. cb =>
  11. user.request.get(`/project/${projectId}`, (error, response, body) => {
  12. if (error != null) {
  13. return cb(error)
  14. }
  15. test(response, body)
  16. cb()
  17. }),
  18. cb =>
  19. user.request.get(
  20. `/project/${projectId}/download/zip`,
  21. (error, response, body) => {
  22. if (error != null) {
  23. return cb(error)
  24. }
  25. test(response, body)
  26. cb()
  27. }
  28. ),
  29. ],
  30. callback
  31. )
  32. }
  33. function tryRenameProjectAccess(user, projectId, test, callback) {
  34. user.request.post(
  35. {
  36. uri: `/project/${projectId}/settings`,
  37. json: {
  38. name: 'new name',
  39. },
  40. },
  41. (error, response, body) => {
  42. if (error != null) {
  43. return callback(error)
  44. }
  45. test(response, body)
  46. callback()
  47. }
  48. )
  49. }
  50. function trySettingsWriteAccess(user, projectId, test, callback) {
  51. async.series(
  52. [
  53. cb =>
  54. user.request.post(
  55. {
  56. uri: `/project/${projectId}/settings`,
  57. json: {
  58. compiler: 'latex',
  59. },
  60. },
  61. (error, response, body) => {
  62. if (error != null) {
  63. return cb(error)
  64. }
  65. test(response, body)
  66. cb()
  67. }
  68. ),
  69. ],
  70. callback
  71. )
  72. }
  73. function tryAdminAccess(user, projectId, test, callback) {
  74. async.series(
  75. [
  76. cb =>
  77. user.request.post(
  78. {
  79. uri: `/project/${projectId}/rename`,
  80. json: {
  81. newProjectName: 'new-name',
  82. },
  83. },
  84. (error, response, body) => {
  85. if (error != null) {
  86. return cb(error)
  87. }
  88. test(response, body)
  89. cb()
  90. }
  91. ),
  92. cb =>
  93. user.request.post(
  94. {
  95. uri: `/project/${projectId}/settings/admin`,
  96. json: {
  97. publicAccessLevel: 'private',
  98. },
  99. },
  100. (error, response, body) => {
  101. if (error != null) {
  102. return cb(error)
  103. }
  104. test(response, body)
  105. cb()
  106. }
  107. ),
  108. ],
  109. callback
  110. )
  111. }
  112. function tryContentAccess(user, projectId, test, callback) {
  113. // The real-time service calls this end point to determine the user's
  114. // permissions.
  115. let userId
  116. if (user.id != null) {
  117. userId = user.id
  118. } else {
  119. userId = 'anonymous-user'
  120. }
  121. request.post(
  122. {
  123. url: `/project/${projectId}/join`,
  124. qs: { user_id: userId },
  125. auth: {
  126. user: settings.apis.web.user,
  127. pass: settings.apis.web.pass,
  128. sendImmediately: true,
  129. },
  130. json: true,
  131. jar: false,
  132. },
  133. (error, response, body) => {
  134. if (error != null) {
  135. return callback(error)
  136. }
  137. test(response, body)
  138. callback()
  139. }
  140. )
  141. }
  142. function expectReadAccess(user, projectId, callback) {
  143. async.series(
  144. [
  145. cb =>
  146. tryReadAccess(
  147. user,
  148. projectId,
  149. (response, body) =>
  150. expect(response.statusCode).to.be.oneOf([200, 204]),
  151. cb
  152. ),
  153. cb =>
  154. tryContentAccess(
  155. user,
  156. projectId,
  157. (response, body) =>
  158. expect(body.privilegeLevel).to.be.oneOf([
  159. 'owner',
  160. 'readAndWrite',
  161. 'readOnly',
  162. ]),
  163. cb
  164. ),
  165. ],
  166. callback
  167. )
  168. }
  169. function expectContentWriteAccess(user, projectId, callback) {
  170. tryContentAccess(
  171. user,
  172. projectId,
  173. (response, body) =>
  174. expect(body.privilegeLevel).to.be.oneOf(['owner', 'readAndWrite']),
  175. callback
  176. )
  177. }
  178. function expectRenameProjectAccess(user, projectId, callback) {
  179. tryRenameProjectAccess(
  180. user,
  181. projectId,
  182. (response, body) => {
  183. expect(response.statusCode).to.be.oneOf([200, 204])
  184. },
  185. callback
  186. )
  187. }
  188. function expectSettingsWriteAccess(user, projectId, callback) {
  189. trySettingsWriteAccess(
  190. user,
  191. projectId,
  192. (response, body) => expect(response.statusCode).to.be.oneOf([200, 204]),
  193. callback
  194. )
  195. }
  196. function expectAdminAccess(user, projectId, callback) {
  197. tryAdminAccess(
  198. user,
  199. projectId,
  200. (response, body) => expect(response.statusCode).to.be.oneOf([200, 204]),
  201. callback
  202. )
  203. }
  204. function expectNoReadAccess(user, projectId, callback) {
  205. async.series(
  206. [
  207. cb =>
  208. tryReadAccess(user, projectId, expectErrorResponse.restricted.html, cb),
  209. cb =>
  210. tryContentAccess(
  211. user,
  212. projectId,
  213. (response, body) => {
  214. expect(response.statusCode).to.equal(403)
  215. expect(body).to.equal('Forbidden')
  216. },
  217. cb
  218. ),
  219. ],
  220. callback
  221. )
  222. }
  223. function expectNoContentWriteAccess(user, projectId, callback) {
  224. tryContentAccess(
  225. user,
  226. projectId,
  227. (response, body) =>
  228. expect(body.privilegeLevel).to.be.oneOf([undefined, null, 'readOnly']),
  229. callback
  230. )
  231. }
  232. function expectNoSettingsWriteAccess(user, projectId, callback) {
  233. trySettingsWriteAccess(
  234. user,
  235. projectId,
  236. expectErrorResponse.restricted.json,
  237. callback
  238. )
  239. }
  240. function expectNoRenameProjectAccess(user, projectId, callback) {
  241. tryRenameProjectAccess(
  242. user,
  243. projectId,
  244. expectErrorResponse.restricted.json,
  245. callback
  246. )
  247. }
  248. function expectNoAdminAccess(user, projectId, callback) {
  249. tryAdminAccess(
  250. user,
  251. projectId,
  252. (response, body) => {
  253. expect(response.statusCode).to.equal(403)
  254. },
  255. callback
  256. )
  257. }
  258. function expectNoAnonymousAdminAccess(user, projectId, callback) {
  259. tryAdminAccess(
  260. user,
  261. projectId,
  262. expectErrorResponse.requireLogin.json,
  263. callback
  264. )
  265. }
  266. function expectChatAccess(user, projectId, callback) {
  267. user.request.get(
  268. {
  269. url: `/project/${projectId}/messages`,
  270. json: true,
  271. },
  272. (error, response) => {
  273. if (error != null) {
  274. return callback(error)
  275. }
  276. expect(response.statusCode).to.equal(200)
  277. callback()
  278. }
  279. )
  280. }
  281. function expectNoChatAccess(user, projectId, callback) {
  282. user.request.get(
  283. {
  284. url: `/project/${projectId}/messages`,
  285. json: true,
  286. },
  287. (error, response) => {
  288. if (error != null) {
  289. return callback(error)
  290. }
  291. expect(response.statusCode).to.equal(403)
  292. callback()
  293. }
  294. )
  295. }
  296. describe('Authorization', function () {
  297. beforeEach(function (done) {
  298. this.timeout(90000)
  299. this.owner = new User()
  300. this.other1 = new User()
  301. this.other2 = new User()
  302. this.anon = new User()
  303. this.site_admin = new User({ email: 'admin@example.com' })
  304. async.parallel(
  305. [
  306. cb => this.owner.login(cb),
  307. cb => this.other1.login(cb),
  308. cb => this.other2.login(cb),
  309. cb => this.anon.getCsrfToken(cb),
  310. cb => {
  311. this.site_admin.login(err => {
  312. if (err != null) {
  313. return cb(err)
  314. }
  315. return this.site_admin.ensureAdmin(cb)
  316. })
  317. },
  318. ],
  319. done
  320. )
  321. })
  322. describe('private project', function () {
  323. beforeEach(function (done) {
  324. this.owner.createProject('private-project', (error, projectId) => {
  325. if (error != null) {
  326. return done(error)
  327. }
  328. this.projectId = projectId
  329. done()
  330. })
  331. })
  332. it('should allow the owner read access to it', function (done) {
  333. expectReadAccess(this.owner, this.projectId, done)
  334. })
  335. it('should allow the owner write access to its content', function (done) {
  336. expectContentWriteAccess(this.owner, this.projectId, done)
  337. })
  338. it('should allow the owner write access to its settings', function (done) {
  339. expectSettingsWriteAccess(this.owner, this.projectId, done)
  340. })
  341. it('should allow the owner to rename the project', function (done) {
  342. expectRenameProjectAccess(this.owner, this.projectId, done)
  343. })
  344. it('should allow the owner admin access to it', function (done) {
  345. expectAdminAccess(this.owner, this.projectId, done)
  346. })
  347. it('should allow the owner user chat messages access', function (done) {
  348. expectChatAccess(this.owner, this.projectId, done)
  349. })
  350. it('should not allow another user read access to the project', function (done) {
  351. expectNoReadAccess(this.other1, this.projectId, done)
  352. })
  353. it('should not allow another user write access to its content', function (done) {
  354. expectNoContentWriteAccess(this.other1, this.projectId, done)
  355. })
  356. it('should not allow another user write access to its settings', function (done) {
  357. expectNoSettingsWriteAccess(this.other1, this.projectId, done)
  358. })
  359. it('should not allow another user to rename the project', function (done) {
  360. expectNoRenameProjectAccess(this.other1, this.projectId, done)
  361. })
  362. it('should not allow another user admin access to it', function (done) {
  363. expectNoAdminAccess(this.other1, this.projectId, done)
  364. })
  365. it('should not allow another user chat messages access', function (done) {
  366. expectNoChatAccess(this.other1, this.projectId, done)
  367. })
  368. it('should not allow anonymous user read access to it', function (done) {
  369. expectNoReadAccess(this.anon, this.projectId, done)
  370. })
  371. it('should not allow anonymous user write access to its content', function (done) {
  372. expectNoContentWriteAccess(this.anon, this.projectId, done)
  373. })
  374. it('should not allow anonymous user write access to its settings', function (done) {
  375. expectNoSettingsWriteAccess(this.anon, this.projectId, done)
  376. })
  377. it('should not allow anonymous user to rename the project', function (done) {
  378. expectNoRenameProjectAccess(this.anon, this.projectId, done)
  379. })
  380. it('should not allow anonymous user admin access to it', function (done) {
  381. expectNoAnonymousAdminAccess(this.anon, this.projectId, done)
  382. })
  383. it('should not allow anonymous user chat messages access', function (done) {
  384. expectNoChatAccess(this.anon, this.projectId, done)
  385. })
  386. it('should allow site admin users read access to it', function (done) {
  387. expectReadAccess(this.site_admin, this.projectId, done)
  388. })
  389. it('should allow site admin users write access to its content', function (done) {
  390. expectContentWriteAccess(this.site_admin, this.projectId, done)
  391. })
  392. it('should allow site admin users write access to its settings', function (done) {
  393. expectSettingsWriteAccess(this.site_admin, this.projectId, done)
  394. })
  395. it('should allow site admin users to rename the project', function (done) {
  396. expectRenameProjectAccess(this.site_admin, this.projectId, done)
  397. })
  398. it('should allow site admin users admin access to it', function (done) {
  399. expectAdminAccess(this.site_admin, this.projectId, done)
  400. })
  401. })
  402. describe('shared project', function () {
  403. beforeEach(function (done) {
  404. this.rw_user = this.other1
  405. this.ro_user = this.other2
  406. this.owner.createProject('private-project', (error, projectId) => {
  407. if (error != null) {
  408. return done(error)
  409. }
  410. this.projectId = projectId
  411. this.owner.addUserToProject(
  412. this.projectId,
  413. this.ro_user,
  414. 'readOnly',
  415. error => {
  416. if (error != null) {
  417. return done(error)
  418. }
  419. this.owner.addUserToProject(
  420. this.projectId,
  421. this.rw_user,
  422. 'readAndWrite',
  423. error => {
  424. if (error != null) {
  425. return done(error)
  426. }
  427. done()
  428. }
  429. )
  430. }
  431. )
  432. })
  433. })
  434. it('should allow the read-only user read access to it', function (done) {
  435. expectReadAccess(this.ro_user, this.projectId, done)
  436. })
  437. it('should allow the read-only user chat messages access', function (done) {
  438. expectChatAccess(this.ro_user, this.projectId, done)
  439. })
  440. it('should not allow the read-only user write access to its content', function (done) {
  441. expectNoContentWriteAccess(this.ro_user, this.projectId, done)
  442. })
  443. it('should not allow the read-only user write access to its settings', function (done) {
  444. expectNoSettingsWriteAccess(this.ro_user, this.projectId, done)
  445. })
  446. it('should not allow the read-only user to rename the project', function (done) {
  447. expectNoRenameProjectAccess(this.ro_user, this.projectId, done)
  448. })
  449. it('should not allow the read-only user admin access to it', function (done) {
  450. expectNoAdminAccess(this.ro_user, this.projectId, done)
  451. })
  452. it('should allow the read-write user read access to it', function (done) {
  453. expectReadAccess(this.rw_user, this.projectId, done)
  454. })
  455. it('should allow the read-write user write access to its content', function (done) {
  456. expectContentWriteAccess(this.rw_user, this.projectId, done)
  457. })
  458. it('should allow the read-write user write access to its settings', function (done) {
  459. expectSettingsWriteAccess(this.rw_user, this.projectId, done)
  460. })
  461. it('should not allow the read-write user to rename the project', function (done) {
  462. expectNoRenameProjectAccess(this.rw_user, this.projectId, done)
  463. })
  464. it('should not allow the read-write user admin access to it', function (done) {
  465. expectNoAdminAccess(this.rw_user, this.projectId, done)
  466. })
  467. it('should allow the read-write user chat messages access', function (done) {
  468. expectChatAccess(this.rw_user, this.projectId, done)
  469. })
  470. })
  471. describe('public read-write project', function () {
  472. beforeEach(function (done) {
  473. this.owner.createProject('public-rw-project', (error, projectId) => {
  474. if (error != null) {
  475. return done(error)
  476. }
  477. this.projectId = projectId
  478. this.owner.makePublic(this.projectId, 'readAndWrite', done)
  479. })
  480. })
  481. it('should allow a user read access to it', function (done) {
  482. expectReadAccess(this.other1, this.projectId, done)
  483. })
  484. it('should allow a user write access to its content', function (done) {
  485. expectContentWriteAccess(this.other1, this.projectId, done)
  486. })
  487. it('should allow a user chat messages access', function (done) {
  488. expectChatAccess(this.other1, this.projectId, done)
  489. })
  490. it('should not allow a user write access to its settings', function (done) {
  491. expectNoSettingsWriteAccess(this.other1, this.projectId, done)
  492. })
  493. it('should not allow a user to rename the project', function (done) {
  494. expectNoRenameProjectAccess(this.other1, this.projectId, done)
  495. })
  496. it('should not allow a user admin access to it', function (done) {
  497. expectNoAdminAccess(this.other1, this.projectId, done)
  498. })
  499. it('should allow an anonymous user read access to it', function (done) {
  500. expectReadAccess(this.anon, this.projectId, done)
  501. })
  502. it('should allow an anonymous user write access to its content', function (done) {
  503. expectContentWriteAccess(this.anon, this.projectId, done)
  504. })
  505. it('should allow an anonymous user chat messages access', function (done) {
  506. expectChatAccess(this.anon, this.projectId, done)
  507. })
  508. it('should not allow an anonymous user write access to its settings', function (done) {
  509. expectNoSettingsWriteAccess(this.anon, this.projectId, done)
  510. })
  511. it('should not allow an anonymous user to rename the project', function (done) {
  512. expectNoRenameProjectAccess(this.anon, this.projectId, done)
  513. })
  514. it('should not allow an anonymous user admin access to it', function (done) {
  515. expectNoAnonymousAdminAccess(this.anon, this.projectId, done)
  516. })
  517. })
  518. describe('public read-only project', function () {
  519. beforeEach(function (done) {
  520. this.owner.createProject('public-ro-project', (error, projectId) => {
  521. if (error != null) {
  522. return done(error)
  523. }
  524. this.projectId = projectId
  525. this.owner.makePublic(this.projectId, 'readOnly', done)
  526. })
  527. })
  528. it('should allow a user read access to it', function (done) {
  529. expectReadAccess(this.other1, this.projectId, done)
  530. })
  531. it('should not allow a user write access to its content', function (done) {
  532. expectNoContentWriteAccess(this.other1, this.projectId, done)
  533. })
  534. it('should not allow a user write access to its settings', function (done) {
  535. expectNoSettingsWriteAccess(this.other1, this.projectId, done)
  536. })
  537. it('should not allow a user to rename the project', function (done) {
  538. expectNoRenameProjectAccess(this.other1, this.projectId, done)
  539. })
  540. it('should not allow a user admin access to it', function (done) {
  541. expectNoAdminAccess(this.other1, this.projectId, done)
  542. })
  543. // NOTE: legacy readOnly access does not count as 'restricted' in the new model
  544. it('should allow a user chat messages access', function (done) {
  545. expectChatAccess(this.other1, this.projectId, done)
  546. })
  547. it('should allow an anonymous user read access to it', function (done) {
  548. expectReadAccess(this.anon, this.projectId, done)
  549. })
  550. it('should not allow an anonymous user write access to its content', function (done) {
  551. expectNoContentWriteAccess(this.anon, this.projectId, done)
  552. })
  553. it('should not allow an anonymous user write access to its settings', function (done) {
  554. expectNoSettingsWriteAccess(this.anon, this.projectId, done)
  555. })
  556. it('should not allow an anonymous user to rename the project', function (done) {
  557. expectNoRenameProjectAccess(this.anon, this.projectId, done)
  558. })
  559. it('should not allow an anonymous user admin access to it', function (done) {
  560. expectNoAnonymousAdminAccess(this.anon, this.projectId, done)
  561. })
  562. it('should not allow an anonymous user chat messages access', function (done) {
  563. expectNoChatAccess(this.anon, this.projectId, done)
  564. })
  565. })
  566. })