AuthorizationTests.js 16 KB

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