TokenAccessHandler.coffee 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. Project = require('../../models/Project').Project
  2. CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
  3. PublicAccessLevels = require '../Authorization/PublicAccessLevels'
  4. PrivilegeLevels = require '../Authorization/PrivilegeLevels'
  5. UserGetter = require '../User/UserGetter'
  6. ObjectId = require("mongojs").ObjectId
  7. Settings = require('settings-sharelatex')
  8. V1Api = require "../V1/V1Api"
  9. crypto = require 'crypto'
  10. module.exports = TokenAccessHandler =
  11. ANONYMOUS_READ_AND_WRITE_ENABLED:
  12. Settings.allowAnonymousReadAndWriteSharing == true
  13. _extractNumericPrefix: (token) ->
  14. token.match(/^(\d+)\w+/)
  15. _getProjectByReadOnlyToken: (token, callback=(err, project)->) ->
  16. Project.findOne {
  17. 'tokens.readOnly': token
  18. }, {_id: 1, tokens: 1, publicAccesLevel: 1, owner_ref: 1}, callback
  19. _getProjectByEitherToken: (token, callback=(err, project)->) ->
  20. TokenAccessHandler._getProjectByReadOnlyToken token, (err, project) ->
  21. return callback(err) if err?
  22. if project?
  23. return callback(null, project)
  24. TokenAccessHandler._getProjectByReadAndWriteToken token, (err, project) ->
  25. return callback(err) if err?
  26. callback(null, project)
  27. _getProjectByReadAndWriteToken: (token, callback=(err, project)->) ->
  28. numericPrefixMatch = TokenAccessHandler._extractNumericPrefix(token)
  29. if !numericPrefixMatch
  30. return callback(null, null)
  31. numerics = numericPrefixMatch[1]
  32. Project.findOne {
  33. 'tokens.readAndWritePrefix': numerics
  34. }, {_id: 1, tokens: 1, publicAccesLevel: 1, owner_ref: 1}, (err, project) ->
  35. return callback(err) if err?
  36. if !project?
  37. return callback(null, null)
  38. if !crypto.timingSafeEqual(new Buffer(token), new Buffer(project.tokens.readAndWrite))
  39. logger.err {token}, "read-and-write token match on numeric section, but not on full token"
  40. return callback(null, null)
  41. callback(null, project)
  42. findProjectWithReadOnlyToken: (token, callback=(err, project, projectExists)->) ->
  43. TokenAccessHandler._getProjectByReadOnlyToken token, (err, project) ->
  44. if err?
  45. return callback(err)
  46. if !project?
  47. return callback(null, null, false) # Project doesn't exist, so we handle differently
  48. if project.publicAccesLevel != PublicAccessLevels.TOKEN_BASED
  49. return callback(null, null, true) # Project does exist, but it isn't token based
  50. return callback(null, project, true)
  51. findProjectWithReadAndWriteToken: (token, callback=(err, project, projectExists)->) ->
  52. TokenAccessHandler._getProjectByReadAndWriteToken token, (err, project) ->
  53. if err?
  54. return callback(err)
  55. if !project?
  56. return callback(null, null, false) # Project doesn't exist, so we handle differently
  57. if project.publicAccesLevel != PublicAccessLevels.TOKEN_BASED
  58. return callback(null, null, true) # Project does exist, but it isn't token based
  59. return callback(null, project, true)
  60. _userIsMember: (userId, projectId, callback=(err, isMember)->) ->
  61. CollaboratorsHandler.isUserInvitedMemberOfProject userId, projectId, callback
  62. findProjectWithHigherAccess: (token, userId, callback=(err, project)->) ->
  63. TokenAccessHandler._getProjectByEitherToken token, (err, project) ->
  64. return callback(err) if err?
  65. if !project?
  66. return callback(null, null)
  67. projectId = project._id
  68. TokenAccessHandler._userIsMember userId, projectId, (err, isMember) ->
  69. return callback(err) if err?
  70. callback(
  71. null,
  72. if isMember == true then project else null
  73. )
  74. addReadOnlyUserToProject: (userId, projectId, callback=(err)->) ->
  75. userId = ObjectId(userId.toString())
  76. projectId = ObjectId(projectId.toString())
  77. Project.update {
  78. _id: projectId
  79. }, {
  80. $addToSet: {tokenAccessReadOnly_refs: userId}
  81. }, callback
  82. addReadAndWriteUserToProject: (userId, projectId, callback=(err)->) ->
  83. userId = ObjectId(userId.toString())
  84. projectId = ObjectId(projectId.toString())
  85. Project.update {
  86. _id: projectId
  87. }, {
  88. $addToSet: {tokenAccessReadAndWrite_refs: userId}
  89. }, callback
  90. grantSessionTokenAccess: (req, projectId, token) ->
  91. if req.session?
  92. if !req.session.anonTokenAccess?
  93. req.session.anonTokenAccess = {}
  94. req.session.anonTokenAccess[projectId.toString()] = token.toString()
  95. getRequestToken: (req, projectId) ->
  96. token = (
  97. req?.session?.anonTokenAccess?[projectId.toString()] or
  98. req?.headers['x-sl-anonymous-access-token']
  99. )
  100. return token
  101. isValidToken: (projectId, token, callback=(err, isValidReadAndWrite, isValidReadOnly)->) ->
  102. if !token
  103. return callback null, false, false
  104. _validate = (project) ->
  105. project? and
  106. project.publicAccesLevel == PublicAccessLevels.TOKEN_BASED and
  107. project._id.toString() == projectId.toString()
  108. TokenAccessHandler.findProjectWithReadAndWriteToken token, (err, readAndWriteProject) ->
  109. return callback(err) if err?
  110. isValidReadAndWrite = _validate(readAndWriteProject)
  111. TokenAccessHandler.findProjectWithReadOnlyToken token, (err, readOnlyProject) ->
  112. return callback(err) if err?
  113. isValidReadOnly = _validate(readOnlyProject)
  114. callback null, isValidReadAndWrite, isValidReadOnly
  115. protectTokens: (project, privilegeLevel) ->
  116. if project? && project.tokens?
  117. if privilegeLevel == PrivilegeLevels.OWNER
  118. return
  119. if privilegeLevel != PrivilegeLevels.READ_AND_WRITE
  120. project.tokens.readAndWrite = ''
  121. project.tokens.readAndWritePrefix = ''
  122. if privilegeLevel != PrivilegeLevels.READ_ONLY
  123. project.tokens.readOnly = ''
  124. getV1DocPublishedInfo: (token, callback = (err, publishedInfo) ->) ->
  125. # default to allowing access
  126. return callback(null, {
  127. allow: true
  128. }) unless Settings.apis?.v1?
  129. V1Api.request { url: "/api/v1/sharelatex/docs/#{token}/is_published" }, (err, response, body) ->
  130. return callback err if err?
  131. callback null, body
  132. getV1DocInfo: (token, v2UserId, callback=(err, info)->) ->
  133. # default to not exported
  134. return callback(null, {
  135. exists: true
  136. exported: false
  137. }) unless Settings.apis?.v1?
  138. UserGetter.getUser v2UserId, { overleaf: 1 }, (err, user) ->
  139. return callback(err) if err?
  140. v1UserId = user.overleaf?.id
  141. V1Api.request { url: "/api/v1/sharelatex/users/#{v1UserId}/docs/#{token}/info" }, (err, response, body) ->
  142. return callback err if err?
  143. callback null, body
  144. module.exports.READ_AND_WRITE_TOKEN_REGEX = /^(\d+)(\w+)$/
  145. module.exports.READ_AND_WRITE_URL_REGEX = /^\/(\d+)(\w+)$/
  146. module.exports.READ_ONLY_TOKEN_REGEX = /^([a-z]{12})$/
  147. module.exports.READ_ONLY_URL_REGEX = /^\/read\/([a-z]{12})$/