UserMembershipMiddleware.js 8.6 KB

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