AuthorizationManager.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. const { callbackify } = require('util')
  2. const { ObjectId } = require('mongodb')
  3. const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
  4. const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
  5. const ProjectGetter = require('../Project/ProjectGetter')
  6. const { User } = require('../../models/User')
  7. const PrivilegeLevels = require('./PrivilegeLevels')
  8. const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
  9. const PublicAccessLevels = require('./PublicAccessLevels')
  10. const Errors = require('../Errors/Errors')
  11. const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper')
  12. const Settings = require('@overleaf/settings')
  13. function isRestrictedUser(userId, privilegeLevel, isTokenMember) {
  14. if (privilegeLevel === PrivilegeLevels.NONE) {
  15. return true
  16. }
  17. return (
  18. privilegeLevel === PrivilegeLevels.READ_ONLY && (isTokenMember || !userId)
  19. )
  20. }
  21. async function isRestrictedUserForProject(userId, projectId, token) {
  22. const privilegeLevel = await getPrivilegeLevelForProject(
  23. userId,
  24. projectId,
  25. token
  26. )
  27. const isTokenMember = await CollaboratorsHandler.promises.userIsTokenMember(
  28. userId,
  29. projectId
  30. )
  31. return isRestrictedUser(userId, privilegeLevel, isTokenMember)
  32. }
  33. async function getPublicAccessLevel(projectId) {
  34. if (!ObjectId.isValid(projectId)) {
  35. throw new Error('invalid project id')
  36. }
  37. // Note, the Project property in the DB is `publicAccesLevel`, without the second `s`
  38. const project = await ProjectGetter.promises.getProject(projectId, {
  39. publicAccesLevel: 1,
  40. })
  41. if (!project) {
  42. throw new Errors.NotFoundError(`no project found with id ${projectId}`)
  43. }
  44. return project.publicAccesLevel
  45. }
  46. /**
  47. * Get the privilege level that the user has for the project.
  48. *
  49. * @param userId - The id of the user that wants to access the project.
  50. * @param projectId - The id of the project to be accessed.
  51. * @param {Object} opts
  52. * @param {boolean} opts.ignoreSiteAdmin - Do not consider whether the user is
  53. * a site admin.
  54. * @param {boolean} opts.ignorePublicAccess - Do not consider the project is
  55. * publicly accessible.
  56. *
  57. * @returns {string|boolean} The privilege level. One of "owner",
  58. * "readAndWrite", "readOnly" or false.
  59. */
  60. async function getPrivilegeLevelForProject(
  61. userId,
  62. projectId,
  63. token,
  64. opts = {}
  65. ) {
  66. if (userId) {
  67. return getPrivilegeLevelForProjectWithUser(userId, projectId, opts)
  68. } else {
  69. return getPrivilegeLevelForProjectWithoutUser(projectId, token, opts)
  70. }
  71. }
  72. // User is present, get their privilege level from database
  73. async function getPrivilegeLevelForProjectWithUser(
  74. userId,
  75. projectId,
  76. opts = {}
  77. ) {
  78. const privilegeLevel =
  79. await CollaboratorsGetter.promises.getMemberIdPrivilegeLevel(
  80. userId,
  81. projectId
  82. )
  83. if (privilegeLevel && privilegeLevel !== PrivilegeLevels.NONE) {
  84. // The user has direct access
  85. return privilegeLevel
  86. }
  87. if (!opts.ignoreSiteAdmin) {
  88. if (await isUserSiteAdmin(userId)) {
  89. return PrivilegeLevels.OWNER
  90. }
  91. }
  92. if (!opts.ignorePublicAccess) {
  93. // Legacy public-access system
  94. // User is present (not anonymous), but does not have direct access
  95. const publicAccessLevel = await getPublicAccessLevel(projectId)
  96. if (publicAccessLevel === PublicAccessLevels.READ_ONLY) {
  97. return PrivilegeLevels.READ_ONLY
  98. }
  99. if (publicAccessLevel === PublicAccessLevels.READ_AND_WRITE) {
  100. return PrivilegeLevels.READ_AND_WRITE
  101. }
  102. }
  103. return PrivilegeLevels.NONE
  104. }
  105. // User is Anonymous, Try Token-based access
  106. async function getPrivilegeLevelForProjectWithoutUser(
  107. projectId,
  108. token,
  109. opts = {}
  110. ) {
  111. const publicAccessLevel = await getPublicAccessLevel(projectId)
  112. if (!opts.ignorePublicAccess) {
  113. if (publicAccessLevel === PublicAccessLevels.READ_ONLY) {
  114. // Legacy public read-only access for anonymous user
  115. return PrivilegeLevels.READ_ONLY
  116. }
  117. if (publicAccessLevel === PublicAccessLevels.READ_AND_WRITE) {
  118. // Legacy public read-write access for anonymous user
  119. return PrivilegeLevels.READ_AND_WRITE
  120. }
  121. }
  122. if (publicAccessLevel === PublicAccessLevels.TOKEN_BASED) {
  123. return getPrivilegeLevelForProjectWithToken(projectId, token)
  124. }
  125. // Deny anonymous user access
  126. return PrivilegeLevels.NONE
  127. }
  128. async function getPrivilegeLevelForProjectWithToken(projectId, token) {
  129. // Anonymous users can have read-only access to token-based projects,
  130. // while read-write access must be logged in,
  131. // unless the `enableAnonymousReadAndWriteSharing` setting is enabled
  132. const { isValidReadAndWrite, isValidReadOnly } =
  133. await TokenAccessHandler.promises.validateTokenForAnonymousAccess(
  134. projectId,
  135. token
  136. )
  137. if (isValidReadOnly) {
  138. // Grant anonymous user read-only access
  139. return PrivilegeLevels.READ_ONLY
  140. }
  141. if (isValidReadAndWrite) {
  142. // Grant anonymous user read-and-write access
  143. return PrivilegeLevels.READ_AND_WRITE
  144. }
  145. // Deny anonymous access
  146. return PrivilegeLevels.NONE
  147. }
  148. async function canUserReadProject(userId, projectId, token) {
  149. const privilegeLevel = await getPrivilegeLevelForProject(
  150. userId,
  151. projectId,
  152. token
  153. )
  154. return [
  155. PrivilegeLevels.OWNER,
  156. PrivilegeLevels.READ_AND_WRITE,
  157. PrivilegeLevels.READ_ONLY,
  158. ].includes(privilegeLevel)
  159. }
  160. async function canUserWriteProjectContent(userId, projectId, token) {
  161. const privilegeLevel = await getPrivilegeLevelForProject(
  162. userId,
  163. projectId,
  164. token
  165. )
  166. return [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE].includes(
  167. privilegeLevel
  168. )
  169. }
  170. async function canUserWriteProjectSettings(userId, projectId, token) {
  171. const privilegeLevel = await getPrivilegeLevelForProject(
  172. userId,
  173. projectId,
  174. token,
  175. { ignorePublicAccess: true }
  176. )
  177. return [PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE].includes(
  178. privilegeLevel
  179. )
  180. }
  181. async function canUserRenameProject(userId, projectId, token) {
  182. const privilegeLevel = await getPrivilegeLevelForProject(
  183. userId,
  184. projectId,
  185. token
  186. )
  187. return privilegeLevel === PrivilegeLevels.OWNER
  188. }
  189. async function canUserAdminProject(userId, projectId, token) {
  190. const privilegeLevel = await getPrivilegeLevelForProject(
  191. userId,
  192. projectId,
  193. token
  194. )
  195. return privilegeLevel === PrivilegeLevels.OWNER
  196. }
  197. async function isUserSiteAdmin(userId) {
  198. if (!userId) {
  199. return false
  200. }
  201. if (!Settings.adminPrivilegeAvailable) return false
  202. const user = await User.findOne({ _id: userId }, { isAdmin: 1 }).exec()
  203. return hasAdminAccess(user)
  204. }
  205. module.exports = {
  206. canUserReadProject: callbackify(canUserReadProject),
  207. canUserWriteProjectContent: callbackify(canUserWriteProjectContent),
  208. canUserWriteProjectSettings: callbackify(canUserWriteProjectSettings),
  209. canUserRenameProject: callbackify(canUserRenameProject),
  210. canUserAdminProject: callbackify(canUserAdminProject),
  211. getPrivilegeLevelForProject: callbackify(getPrivilegeLevelForProject),
  212. isRestrictedUser,
  213. isRestrictedUserForProject: callbackify(isRestrictedUserForProject),
  214. isUserSiteAdmin: callbackify(isUserSiteAdmin),
  215. promises: {
  216. canUserReadProject,
  217. canUserWriteProjectContent,
  218. canUserWriteProjectSettings,
  219. canUserRenameProject,
  220. canUserAdminProject,
  221. getPrivilegeLevelForProject,
  222. isRestrictedUserForProject,
  223. isUserSiteAdmin,
  224. },
  225. }