AuthorizationManager.js 7.4 KB

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