AuthorizationTests.js 15 KB

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