AuthorizationMiddleware.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. const AuthorizationManager = require('./AuthorizationManager')
  2. const logger = require('@overleaf/logger')
  3. const { ObjectId } = require('mongodb')
  4. const Errors = require('../Errors/Errors')
  5. const HttpErrorHandler = require('../Errors/HttpErrorHandler')
  6. const AuthenticationController = require('../Authentication/AuthenticationController')
  7. const SessionManager = require('../Authentication/SessionManager')
  8. const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
  9. const { expressify } = require('../../util/promises')
  10. const {
  11. canRedirectToAdminDomain,
  12. } = require('../Helpers/AdminAuthorizationHelper')
  13. const { getSafeAdminDomainRedirect } = require('../Helpers/UrlHelper')
  14. function handleAdminDomainRedirect(req, res) {
  15. if (canRedirectToAdminDomain(SessionManager.getSessionUser(req.session))) {
  16. logger.warn({ req }, 'redirecting admin user to admin domain')
  17. res.redirect(getSafeAdminDomainRedirect(req.originalUrl))
  18. return true
  19. }
  20. return false
  21. }
  22. async function ensureUserCanReadMultipleProjects(req, res, next) {
  23. const projectIds = (req.query.project_ids || '').split(',')
  24. const userId = _getUserId(req)
  25. for (const projectId of projectIds) {
  26. const token = TokenAccessHandler.getRequestToken(req, projectId)
  27. const canRead = await AuthorizationManager.promises.canUserReadProject(
  28. userId,
  29. projectId,
  30. token
  31. )
  32. if (!canRead) {
  33. return _redirectToRestricted(req, res, next)
  34. }
  35. }
  36. next()
  37. }
  38. async function blockRestrictedUserFromProject(req, res, next) {
  39. const projectId = _getProjectId(req)
  40. const userId = _getUserId(req)
  41. const token = TokenAccessHandler.getRequestToken(req, projectId)
  42. const isRestrictedUser =
  43. await AuthorizationManager.promises.isRestrictedUserForProject(
  44. userId,
  45. projectId,
  46. token
  47. )
  48. if (isRestrictedUser) {
  49. return HttpErrorHandler.forbidden(req, res)
  50. }
  51. next()
  52. }
  53. async function ensureUserCanReadProject(req, res, next) {
  54. const projectId = _getProjectId(req)
  55. const userId = _getUserId(req)
  56. const token = TokenAccessHandler.getRequestToken(req, projectId)
  57. const canRead = await AuthorizationManager.promises.canUserReadProject(
  58. userId,
  59. projectId,
  60. token
  61. )
  62. if (canRead) {
  63. logger.log({ userId, projectId }, 'allowing user read access to project')
  64. return next()
  65. }
  66. logger.log({ userId, projectId }, 'denying user read access to project')
  67. HttpErrorHandler.forbidden(req, res)
  68. }
  69. async function ensureUserCanWriteProjectSettings(req, res, next) {
  70. const projectId = _getProjectId(req)
  71. const userId = _getUserId(req)
  72. const token = TokenAccessHandler.getRequestToken(req, projectId)
  73. if (req.body.name != null) {
  74. const canRename = await AuthorizationManager.promises.canUserRenameProject(
  75. userId,
  76. projectId,
  77. token
  78. )
  79. if (!canRename) {
  80. return HttpErrorHandler.forbidden(req, res)
  81. }
  82. }
  83. const otherParams = Object.keys(req.body).filter(x => x !== 'name')
  84. if (otherParams.length > 0) {
  85. const canWrite =
  86. await AuthorizationManager.promises.canUserWriteProjectSettings(
  87. userId,
  88. projectId,
  89. token
  90. )
  91. if (!canWrite) {
  92. return HttpErrorHandler.forbidden(req, res)
  93. }
  94. }
  95. next()
  96. }
  97. async function ensureUserCanWriteProjectContent(req, res, next) {
  98. const projectId = _getProjectId(req)
  99. const userId = _getUserId(req)
  100. const token = TokenAccessHandler.getRequestToken(req, projectId)
  101. const canWrite =
  102. await AuthorizationManager.promises.canUserWriteProjectContent(
  103. userId,
  104. projectId,
  105. token
  106. )
  107. if (canWrite) {
  108. logger.log(
  109. { userId, projectId },
  110. 'allowing user write access to project content'
  111. )
  112. return next()
  113. }
  114. logger.log(
  115. { userId, projectId },
  116. 'denying user write access to project settings'
  117. )
  118. HttpErrorHandler.forbidden(req, res)
  119. }
  120. async function ensureUserCanAdminProject(req, res, next) {
  121. const projectId = _getProjectId(req)
  122. const userId = _getUserId(req)
  123. const token = TokenAccessHandler.getRequestToken(req, projectId)
  124. const canAdmin = await AuthorizationManager.promises.canUserAdminProject(
  125. userId,
  126. projectId,
  127. token
  128. )
  129. if (canAdmin) {
  130. logger.log({ userId, projectId }, 'allowing user admin access to project')
  131. return next()
  132. }
  133. logger.log({ userId, projectId }, 'denying user admin access to project')
  134. HttpErrorHandler.forbidden(req, res)
  135. }
  136. async function ensureUserIsSiteAdmin(req, res, next) {
  137. const userId = _getUserId(req)
  138. if (await AuthorizationManager.promises.isUserSiteAdmin(userId)) {
  139. logger.log({ userId }, 'allowing user admin access to site')
  140. return next()
  141. }
  142. if (handleAdminDomainRedirect(req, res)) return
  143. logger.log({ userId }, 'denying user admin access to site')
  144. _redirectToRestricted(req, res, next)
  145. }
  146. function _getProjectId(req) {
  147. const projectId = req.params.project_id || req.params.Project_id
  148. if (!projectId) {
  149. throw new Error('Expected project_id in request parameters')
  150. }
  151. if (!ObjectId.isValid(projectId)) {
  152. throw new Errors.NotFoundError(`invalid projectId: ${projectId}`)
  153. }
  154. return projectId
  155. }
  156. function _getUserId(req) {
  157. return (
  158. SessionManager.getLoggedInUserId(req.session) ||
  159. (req.oauth_user && req.oauth_user._id) ||
  160. null
  161. )
  162. }
  163. function _redirectToRestricted(req, res, next) {
  164. // TODO: move this to throwing ForbiddenError
  165. res.redirect(`/restricted?from=${encodeURIComponent(res.locals.currentUrl)}`)
  166. }
  167. function restricted(req, res, next) {
  168. if (SessionManager.isUserLoggedIn(req.session)) {
  169. return res.render('user/restricted', { title: 'restricted' })
  170. }
  171. const { from } = req.query
  172. logger.log({ from }, 'redirecting to login')
  173. if (from) {
  174. AuthenticationController.setRedirectInSession(req, from)
  175. }
  176. res.redirect('/login')
  177. }
  178. module.exports = {
  179. ensureUserCanReadMultipleProjects: expressify(
  180. ensureUserCanReadMultipleProjects
  181. ),
  182. blockRestrictedUserFromProject: expressify(blockRestrictedUserFromProject),
  183. ensureUserCanReadProject: expressify(ensureUserCanReadProject),
  184. ensureUserCanWriteProjectSettings: expressify(
  185. ensureUserCanWriteProjectSettings
  186. ),
  187. ensureUserCanWriteProjectContent: expressify(
  188. ensureUserCanWriteProjectContent
  189. ),
  190. ensureUserCanAdminProject: expressify(ensureUserCanAdminProject),
  191. ensureUserIsSiteAdmin: expressify(ensureUserIsSiteAdmin),
  192. handleAdminDomainRedirect,
  193. restricted,
  194. }