UserMembershipAuthorizationTests.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* eslint-disable
  2. handle-callback-err,
  3. max-len,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const sinon = require('sinon')
  15. const chai = require('chai')
  16. const { expect } = require('chai')
  17. const modulePath =
  18. '../../../../app/src/Features/UserMembership/UserMembershipAuthorization.js'
  19. const SandboxedModule = require('sandboxed-module')
  20. const MockRequest = require('../helpers/MockRequest')
  21. const EntityConfigs = require('../../../../app/src/Features/UserMembership/UserMembershipEntityConfigs')
  22. const Errors = require('../../../../app/src/Features/Errors/Errors')
  23. describe('UserMembershipAuthorization', function() {
  24. beforeEach(function() {
  25. this.req = new MockRequest()
  26. this.req.params.id = 'mock-entity-id'
  27. this.user = { _id: 'mock-user-id' }
  28. this.subscription = { _id: 'mock-subscription-id' }
  29. this.AuthenticationController = {
  30. getSessionUser: sinon.stub().returns(this.user)
  31. }
  32. this.UserMembershipHandler = {
  33. getEntity: sinon.stub().yields(null, this.subscription),
  34. getEntityWithoutAuthorizationCheck: sinon
  35. .stub()
  36. .yields(null, this.subscription)
  37. }
  38. this.AuthorizationMiddleware = {
  39. redirectToRestricted: sinon.stub().yields(),
  40. ensureUserIsSiteAdmin: sinon.stub().yields()
  41. }
  42. return (this.UserMembershipAuthorization = SandboxedModule.require(
  43. modulePath,
  44. {
  45. requires: {
  46. '../Authentication/AuthenticationController': this
  47. .AuthenticationController,
  48. '../Authorization/AuthorizationMiddleware': this
  49. .AuthorizationMiddleware,
  50. './UserMembershipHandler': this.UserMembershipHandler,
  51. './EntityConfigs': EntityConfigs,
  52. '../Errors/Errors': Errors,
  53. request: (this.request = sinon.stub().yields(null, null, {})),
  54. 'logger-sharelatex': {
  55. log() {},
  56. err() {}
  57. }
  58. }
  59. }
  60. ))
  61. })
  62. describe('requireAccessToEntity', function() {
  63. it('get entity', function(done) {
  64. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  65. this.req,
  66. null,
  67. error => {
  68. expect(error).to.not.exist
  69. sinon.assert.calledWithMatch(
  70. this.UserMembershipHandler.getEntity,
  71. this.req.params.id,
  72. { modelName: 'Subscription' },
  73. this.user
  74. )
  75. expect(this.req.entity).to.equal(this.subscription)
  76. expect(this.req.entityConfig).to.exist
  77. return done()
  78. }
  79. )
  80. })
  81. it('handle entity not found as non-admin', function(done) {
  82. this.UserMembershipHandler.getEntity.yields(null, null)
  83. this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields(
  84. null,
  85. null
  86. )
  87. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  88. this.req,
  89. null,
  90. error => {
  91. expect(error).to.exist
  92. expect(error).to.be.instanceof(Error)
  93. expect(error.constructor.name).to.equal('NotFoundError')
  94. sinon.assert.called(this.UserMembershipHandler.getEntity)
  95. expect(this.req.entity).to.not.exist
  96. return done()
  97. }
  98. )
  99. })
  100. it('handle entity not found an admin can create', function(done) {
  101. this.user.isAdmin = true
  102. this.UserMembershipHandler.getEntity.yields(null, null)
  103. this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields(
  104. null,
  105. null
  106. )
  107. return this.UserMembershipAuthorization.requirePublisherMetricsAccess(
  108. this.req,
  109. {
  110. redirect: path => {
  111. expect(path).to.exist
  112. expect(path).to.match(/create/)
  113. return done()
  114. }
  115. }
  116. )
  117. })
  118. it('handle entity not found a non-admin can create', function(done) {
  119. this.user.staffAccess = { institutionManagement: true }
  120. this.UserMembershipHandler.getEntity.yields(null, null)
  121. this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields(
  122. null,
  123. null
  124. )
  125. return this.UserMembershipAuthorization.requirePublisherMetricsAccess(
  126. this.req,
  127. {
  128. redirect: path => {
  129. expect(path).to.exist
  130. expect(path).to.match(/create/)
  131. return done()
  132. }
  133. }
  134. )
  135. })
  136. it('handle entity not found an admin cannot create', function(done) {
  137. this.user.isAdmin = true
  138. this.UserMembershipHandler.getEntity.yields(null, null)
  139. this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields(
  140. null,
  141. null
  142. )
  143. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  144. this.req,
  145. null,
  146. error => {
  147. expect(error).to.exist
  148. expect(error).to.be.instanceof(Error)
  149. expect(error.constructor.name).to.equal('NotFoundError')
  150. return done()
  151. }
  152. )
  153. })
  154. it('handle entity no access', function(done) {
  155. this.UserMembershipHandler.getEntity.yields(null, null)
  156. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  157. this.req,
  158. null,
  159. error => {
  160. sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted)
  161. return done()
  162. }
  163. )
  164. })
  165. it('handle anonymous user', function(done) {
  166. this.AuthenticationController.getSessionUser.returns(null)
  167. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  168. this.req,
  169. null,
  170. error => {
  171. expect(error).to.not.exist
  172. sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted)
  173. sinon.assert.notCalled(this.UserMembershipHandler.getEntity)
  174. expect(this.req.entity).to.not.exist
  175. return done()
  176. }
  177. )
  178. })
  179. return it('checks user is staff if required', function(done) {
  180. return this.UserMembershipAuthorization.requireInstitutionManagementStaffAccess(
  181. this.req,
  182. null,
  183. error => {
  184. expect(error).to.not.exist
  185. sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted)
  186. sinon.assert.notCalled(this.UserMembershipHandler.getEntity)
  187. expect(this.req.entity).to.not.exist
  188. return done()
  189. }
  190. )
  191. })
  192. })
  193. return describe('requireEntityAccess', function() {
  194. it('handle team access', function(done) {
  195. return this.UserMembershipAuthorization.requireTeamMetricsAccess(
  196. this.req,
  197. null,
  198. error => {
  199. expect(error).to.not.exist
  200. sinon.assert.calledWithMatch(
  201. this.UserMembershipHandler.getEntity,
  202. this.req.params.id,
  203. { fields: { primaryKey: 'overleaf.id' } }
  204. )
  205. return done()
  206. }
  207. )
  208. })
  209. it('handle group access', function(done) {
  210. return this.UserMembershipAuthorization.requireGroupMetricsAccess(
  211. this.req,
  212. null,
  213. error => {
  214. expect(error).to.not.exist
  215. sinon.assert.calledWithMatch(
  216. this.UserMembershipHandler.getEntity,
  217. this.req.params.id,
  218. { translations: { title: 'group_account' } }
  219. )
  220. return done()
  221. }
  222. )
  223. })
  224. it('handle group managers access', function(done) {
  225. return this.UserMembershipAuthorization.requireGroupManagersManagementAccess(
  226. this.req,
  227. null,
  228. error => {
  229. expect(error).to.not.exist
  230. sinon.assert.calledWithMatch(
  231. this.UserMembershipHandler.getEntity,
  232. this.req.params.id,
  233. { translations: { subtitle: 'managers_management' } }
  234. )
  235. return done()
  236. }
  237. )
  238. })
  239. it('handle institution access', function(done) {
  240. return this.UserMembershipAuthorization.requireInstitutionMetricsAccess(
  241. this.req,
  242. null,
  243. error => {
  244. expect(error).to.not.exist
  245. sinon.assert.calledWithMatch(
  246. this.UserMembershipHandler.getEntity,
  247. this.req.params.id,
  248. { modelName: 'Institution' }
  249. )
  250. return done()
  251. }
  252. )
  253. })
  254. it('handle template with brand access', function(done) {
  255. const templateData = {
  256. id: 123,
  257. title: 'Template Title',
  258. brand: { slug: 'brand-slug' }
  259. }
  260. this.request.yields(
  261. null,
  262. { statusCode: 200 },
  263. JSON.stringify(templateData)
  264. )
  265. return this.UserMembershipAuthorization.requireTemplateMetricsAccess(
  266. this.req,
  267. null,
  268. error => {
  269. expect(error).to.not.exist
  270. sinon.assert.calledWithMatch(
  271. this.UserMembershipHandler.getEntity,
  272. 'brand-slug',
  273. { modelName: 'Publisher' }
  274. )
  275. return done()
  276. }
  277. )
  278. })
  279. it('handle template without brand access', function(done) {
  280. const templateData = {
  281. id: 123,
  282. title: 'Template Title',
  283. brand: null
  284. }
  285. this.request.yields(
  286. null,
  287. { statusCode: 200 },
  288. JSON.stringify(templateData)
  289. )
  290. return this.UserMembershipAuthorization.requireTemplateMetricsAccess(
  291. this.req,
  292. null,
  293. error => {
  294. expect(error).to.not.exist
  295. sinon.assert.notCalled(this.UserMembershipHandler.getEntity)
  296. sinon.assert.calledOnce(
  297. this.AuthorizationMiddleware.ensureUserIsSiteAdmin
  298. )
  299. return done()
  300. }
  301. )
  302. })
  303. return it('handle graph access', function(done) {
  304. this.req.query.resource_id = 'mock-resource-id'
  305. this.req.query.resource_type = 'institution'
  306. const middleware = this.UserMembershipAuthorization.requireGraphAccess
  307. return middleware(this.req, null, error => {
  308. expect(error).to.not.exist
  309. sinon.assert.calledWithMatch(
  310. this.UserMembershipHandler.getEntity,
  311. this.req.query.resource_id,
  312. { modelName: 'Institution' }
  313. )
  314. return done()
  315. })
  316. })
  317. })
  318. })