UserMembershipMiddleware.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. const { expressify } = require('../../util/promises')
  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 HttpErrorHandler = require('../Errors/HttpErrorHandler')
  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. const 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. fetchPublisherFromTemplate(),
  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 HttpErrorHandler.notFound(req, res, '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 HttpErrorHandler.notFound(
  159. req,
  160. res,
  161. `incorrect entity name: ${entityName}`
  162. )
  163. }
  164. // run the list of middleware functions in series. This is essencially
  165. // a poor man's middleware runner
  166. async.eachSeries(middleware, (fn, callback) => fn(req, res, callback), next)
  167. },
  168. }
  169. module.exports = UserMembershipMiddleware
  170. // fetch entity config and set it in the request
  171. function fetchEntityConfig(entityName) {
  172. return (req, res, next) => {
  173. const entityConfig = EntityConfigs[entityName]
  174. req.entityName = entityName
  175. req.entityConfig = entityConfig
  176. next()
  177. }
  178. }
  179. // fetch the entity with id and config, and set it in the request
  180. function fetchEntity() {
  181. return expressify(async (req, res, next) => {
  182. const entity =
  183. await UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck(
  184. req.params.id,
  185. req.entityConfig
  186. )
  187. req.entity = entity
  188. next()
  189. })
  190. }
  191. function fetchPublisherFromTemplate() {
  192. return (req, res, next) => {
  193. if (req.template.brand.slug) {
  194. // set the id as the publisher's id as it's the entity used for access
  195. // control
  196. req.params.id = req.template.brand.slug
  197. return fetchEntity()(req, res, next)
  198. } else {
  199. return next()
  200. }
  201. }
  202. }
  203. // ensure an entity was found, or fail with 404
  204. function requireEntity() {
  205. return (req, res, next) => {
  206. if (req.entity) {
  207. return next()
  208. }
  209. throw new Errors.NotFoundError(
  210. `no '${req.entityName}' entity with '${req.params.id}'`
  211. )
  212. }
  213. }
  214. // ensure an entity was found or redirect to entity creation page if the user
  215. // has permissions to create the entity, or fail with 404
  216. function requireEntityOrCreate(creationStaffAccess) {
  217. return (req, res, next) => {
  218. if (req.entity) {
  219. return next()
  220. }
  221. if (UserMembershipAuthorization.hasStaffAccess(creationStaffAccess)(req)) {
  222. res.redirect(`/entities/${req.entityName}/create/${req.params.id}`)
  223. return
  224. }
  225. throw new Errors.NotFoundError(
  226. `no '${req.entityName}' entity with '${req.params.id}'`
  227. )
  228. }
  229. }
  230. // fetch the template from v1, and set it in the request
  231. function fetchV1Template() {
  232. return expressify(async (req, res, next) => {
  233. const templateId = req.params.id
  234. const body = await TemplatesManager.promises.fetchFromV1(templateId)
  235. req.template = {
  236. id: body.id,
  237. title: body.title,
  238. brand: body.brand,
  239. }
  240. next()
  241. })
  242. }
  243. // ensure a template was found, or fail with 404
  244. function requireV1Template() {
  245. return (req, res, next) => {
  246. if (req.template.id) {
  247. return next()
  248. }
  249. throw new Errors.NotFoundError('no template found')
  250. }
  251. }
  252. // run a serie of synchronous access functions and call `next` if any of the
  253. // retur values is truly. Redirect to restricted otherwise
  254. function allowAccessIfAny(accessFunctions) {
  255. return (req, res, next) => {
  256. for (const accessFunction of accessFunctions) {
  257. if (accessFunction(req)) {
  258. return next()
  259. }
  260. }
  261. HttpErrorHandler.forbidden(req, res)
  262. }
  263. }