AuthorizationTests.js 16 KB

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