UserMembershipMiddleware.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. const expressify = require('../../util/expressify')
  2. const async = require('async')
  3. const UserMembershipAuthorization = require('./UserMembershipAuthorization')
  4. const AuthenticationController = require('../Authentication/AuthenticationController')
  5. const UserMembershipHandler = require('./UserMembershipHandler')
  6. const EntityConfigs = require('./UserMembershipEntityConfigs')
  7. const Errors = require('../Errors/Errors')
  8. const HttpErrors = require('@overleaf/o-error/http')
  9. const TemplatesManager = require('../Templates/TemplatesManager')
  10. // set of middleware arrays or functions that checks user access to an entity
  11. // (publisher, institution, group, template, etc.)
  12. let UserMembershipMiddleware = {
  13. requireTeamMetricsAccess: [
  14. AuthenticationController.requireLogin(),
  15. fetchEntityConfig('team'),
  16. fetchEntity(),
  17. requireEntity(),
  18. restrictAccess([
  19. UserMembershipAuthorization.hasEntityAccess(),
  20. UserMembershipAuthorization.hasStaffAccess('groupMetrics')
  21. ])
  22. ],
  23. requireGroupManagementAccess: [
  24. AuthenticationController.requireLogin(),
  25. fetchEntityConfig('group'),
  26. fetchEntity(),
  27. requireEntity(),
  28. restrictAccess([
  29. UserMembershipAuthorization.hasEntityAccess(),
  30. UserMembershipAuthorization.hasStaffAccess('groupManagement')
  31. ])
  32. ],
  33. requireGroupMetricsAccess: [
  34. AuthenticationController.requireLogin(),
  35. fetchEntityConfig('group'),
  36. fetchEntity(),
  37. requireEntity(),
  38. restrictAccess([
  39. UserMembershipAuthorization.hasEntityAccess(),
  40. UserMembershipAuthorization.hasStaffAccess('groupMetrics')
  41. ])
  42. ],
  43. requireGroupManagersManagementAccess: [
  44. AuthenticationController.requireLogin(),
  45. fetchEntityConfig('groupManagers'),
  46. fetchEntity(),
  47. requireEntity(),
  48. restrictAccess([
  49. UserMembershipAuthorization.hasEntityAccess(),
  50. UserMembershipAuthorization.hasStaffAccess('groupManagement')
  51. ])
  52. ],
  53. requireInstitutionMetricsAccess: [
  54. AuthenticationController.requireLogin(),
  55. fetchEntityConfig('institution'),
  56. fetchEntity(),
  57. requireEntityOrCreate('institutionManagement'),
  58. restrictAccess([
  59. UserMembershipAuthorization.hasEntityAccess(),
  60. UserMembershipAuthorization.hasStaffAccess('institutionMetrics')
  61. ])
  62. ],
  63. requireInstitutionManagementAccess: [
  64. AuthenticationController.requireLogin(),
  65. fetchEntityConfig('institution'),
  66. fetchEntity(),
  67. requireEntityOrCreate('institutionManagement'),
  68. restrictAccess([
  69. UserMembershipAuthorization.hasEntityAccess(),
  70. UserMembershipAuthorization.hasStaffAccess('institutionManagement')
  71. ])
  72. ],
  73. requireInstitutionManagementStaffAccess: [
  74. AuthenticationController.requireLogin(),
  75. restrictAccess([
  76. UserMembershipAuthorization.hasStaffAccess('institutionManagement')
  77. ]),
  78. fetchEntityConfig('institution'),
  79. fetchEntity(),
  80. requireEntityOrCreate('institutionManagement')
  81. ],
  82. requirePublisherMetricsAccess: [
  83. AuthenticationController.requireLogin(),
  84. fetchEntityConfig('publisher'),
  85. fetchEntity(),
  86. requireEntityOrCreate('publisherManagement'),
  87. restrictAccess([
  88. UserMembershipAuthorization.hasEntityAccess(),
  89. UserMembershipAuthorization.hasStaffAccess('publisherMetrics')
  90. ])
  91. ],
  92. requirePublisherManagementAccess: [
  93. AuthenticationController.requireLogin(),
  94. fetchEntityConfig('publisher'),
  95. fetchEntity(),
  96. requireEntityOrCreate('publisherManagement'),
  97. restrictAccess([
  98. UserMembershipAuthorization.hasEntityAccess(),
  99. UserMembershipAuthorization.hasStaffAccess('publisherManagement')
  100. ])
  101. ],
  102. requireConversionMetricsAccess: [
  103. AuthenticationController.requireLogin(),
  104. fetchEntityConfig('publisher'),
  105. fetchEntity(),
  106. requireEntityOrCreate('publisherManagement'),
  107. restrictAccess([
  108. UserMembershipAuthorization.hasEntityAccess(),
  109. UserMembershipAuthorization.hasStaffAccess('publisherMetrics')
  110. ])
  111. ],
  112. requireAdminMetricsAccess: [
  113. AuthenticationController.requireLogin(),
  114. restrictAccess([UserMembershipAuthorization.hasStaffAccess('adminMetrics')])
  115. ],
  116. requireTemplateMetricsAccess: [
  117. AuthenticationController.requireLogin(),
  118. fetchV1Template(),
  119. requireV1Template(),
  120. fetchEntityConfig('publisher'),
  121. fetchEntity(), // at this point the entity is the template's publisher, if any
  122. restrictAccess([
  123. UserMembershipAuthorization.hasEntityAccess(),
  124. UserMembershipAuthorization.hasStaffAccess('publisherMetrics')
  125. ])
  126. ],
  127. requirePublisherCreationAccess: [
  128. AuthenticationController.requireLogin(),
  129. restrictAccess([
  130. UserMembershipAuthorization.hasStaffAccess('publisherManagement')
  131. ]),
  132. fetchEntityConfig('publisher')
  133. ],
  134. requireInstitutionCreationAccess: [
  135. AuthenticationController.requireLogin(),
  136. restrictAccess([
  137. UserMembershipAuthorization.hasStaffAccess('institutionManagement')
  138. ]),
  139. fetchEntityConfig('institution')
  140. ],
  141. // graphs access is an edge-case:
  142. // - the entity id is in `req.query.resource_id`. It must be set as
  143. // `req.params.id`
  144. // - the entity name is in `req.query.resource_type` and is used to find the
  145. // require middleware depending on the entity name
  146. requireGraphAccess(req, res, next) {
  147. req.params.id = req.query.resource_id
  148. let entityName = req.query.resource_type
  149. entityName = entityName.charAt(0).toUpperCase() + entityName.slice(1)
  150. // run the list of middleware functions in series. This is essencially
  151. // a poor man's middleware runner
  152. async.eachSeries(
  153. UserMembershipMiddleware[`require${entityName}MetricsAccess`],
  154. (fn, callback) => fn(req, res, callback),
  155. next
  156. )
  157. }
  158. }
  159. module.exports = UserMembershipMiddleware
  160. // fetch entity config and set it in the request
  161. function fetchEntityConfig(entityName) {
  162. return (req, res, next) => {
  163. const entityConfig = EntityConfigs[entityName]
  164. req.entityName = entityName
  165. req.entityConfig = entityConfig
  166. next()
  167. }
  168. }
  169. // fetch the entity with id and config, and set it in the request
  170. function fetchEntity() {
  171. return expressify(async (req, res, next) => {
  172. let entity = await UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck(
  173. req.params.id,
  174. req.entityConfig
  175. )
  176. req.entity = entity
  177. next()
  178. })
  179. }
  180. // ensure an entity was found, or fail with 404
  181. function requireEntity() {
  182. return (req, res, next) => {
  183. if (req.entity) {
  184. return next()
  185. }
  186. throw new Errors.NotFoundError(
  187. `no '${req.entityName}' entity with '${req.params.id}'`
  188. )
  189. }
  190. }
  191. // ensure an entity was found or redirect to entity creation page if the user
  192. // has permissions to create the entity, or fail with 404
  193. function requireEntityOrCreate(creationStaffAccess) {
  194. return (req, res, next) => {
  195. if (req.entity) {
  196. return next()
  197. }
  198. if (UserMembershipAuthorization.hasStaffAccess(creationStaffAccess)(req)) {
  199. res.redirect(`/entities/${req.entityName}/create/${req.params.id}`)
  200. return
  201. }
  202. throw new Errors.NotFoundError(
  203. `no '${req.entityName}' entity with '${req.params.id}'`
  204. )
  205. }
  206. }
  207. // fetch the template from v1, and set it in the request
  208. function fetchV1Template() {
  209. return expressify(async (req, res, next) => {
  210. const templateId = req.params.id
  211. const body = await TemplatesManager.promises.fetchFromV1(templateId)
  212. req.template = {
  213. id: body.id,
  214. title: body.title,
  215. brand: body.brand
  216. }
  217. if (req.template.brand.slug) {
  218. // set the id as the publisher's id as it's the entity used for access
  219. // control
  220. req.params.id = req.template.brand.slug
  221. }
  222. next()
  223. })
  224. }
  225. // ensure a template was found, or fail with 404
  226. function requireV1Template() {
  227. return (req, res, next) => {
  228. if (req.template.id) {
  229. return next()
  230. }
  231. throw new Errors.NotFoundError('no template found')
  232. }
  233. }
  234. // run a serie of synchronous access functions and call `next` if any of the
  235. // retur values is truly. Redirect to restricted otherwise
  236. function restrictAccess(accessFunctions) {
  237. return (req, res, next) => {
  238. for (let accessFunction of accessFunctions) {
  239. if (accessFunction(req)) {
  240. return next()
  241. }
  242. }
  243. next(new HttpErrors.ForbiddenError({}))
  244. }
  245. }