OwnershipTransferHandler.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. const logger = require('logger-sharelatex')
  2. const { Project } = require('../../models/Project')
  3. const ProjectGetter = require('../Project/ProjectGetter')
  4. const UserGetter = require('../User/UserGetter')
  5. const CollaboratorsHandler = require('./CollaboratorsHandler')
  6. const EmailHandler = require('../Email/EmailHandler')
  7. const Errors = require('../Errors/Errors')
  8. const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
  9. const TpdsProjectFlusher = require('../ThirdPartyDataStore/TpdsProjectFlusher')
  10. const ProjectAuditLogHandler = require('../Project/ProjectAuditLogHandler')
  11. module.exports = {
  12. promises: { transferOwnership }
  13. }
  14. async function transferOwnership(projectId, newOwnerId, options = {}) {
  15. const { allowTransferToNonCollaborators, sessionUserId } = options
  16. // Fetch project and user
  17. const [project, newOwner] = await Promise.all([
  18. _getProject(projectId),
  19. _getUser(newOwnerId)
  20. ])
  21. // Exit early if the transferee is already the project owner
  22. const previousOwnerId = project.owner_ref
  23. if (previousOwnerId.equals(newOwnerId)) {
  24. return
  25. }
  26. // Check that user is already a collaborator
  27. if (
  28. !allowTransferToNonCollaborators &&
  29. !_userIsCollaborator(newOwner, project)
  30. ) {
  31. throw new Errors.UserNotCollaboratorError({ info: { userId: newOwnerId } })
  32. }
  33. // Transfer ownership
  34. await ProjectAuditLogHandler.promises.addEntry(
  35. projectId,
  36. 'transfer-ownership',
  37. sessionUserId,
  38. { previousOwnerId, newOwnerId }
  39. )
  40. await _transferOwnership(projectId, previousOwnerId, newOwnerId)
  41. // Flush project to TPDS
  42. await TpdsProjectFlusher.promises.flushProjectToTpds(projectId)
  43. // Send confirmation emails
  44. const previousOwner = await UserGetter.promises.getUser(previousOwnerId)
  45. await _sendEmails(project, previousOwner, newOwner)
  46. }
  47. async function _getProject(projectId) {
  48. const project = await ProjectGetter.promises.getProject(projectId, {
  49. owner_ref: 1,
  50. collaberator_refs: 1,
  51. name: 1
  52. })
  53. if (project == null) {
  54. throw new Errors.ProjectNotFoundError({ info: { projectId } })
  55. }
  56. return project
  57. }
  58. async function _getUser(userId) {
  59. const user = await UserGetter.promises.getUser(userId)
  60. if (user == null) {
  61. throw new Errors.UserNotFoundError({ info: { userId } })
  62. }
  63. return user
  64. }
  65. function _userIsCollaborator(user, project) {
  66. const collaboratorIds = project.collaberator_refs || []
  67. return collaboratorIds.some(collaboratorId => collaboratorId.equals(user._id))
  68. }
  69. async function _transferOwnership(projectId, previousOwnerId, newOwnerId) {
  70. await CollaboratorsHandler.promises.removeUserFromProject(
  71. projectId,
  72. newOwnerId
  73. )
  74. await Project.updateOne(
  75. { _id: projectId },
  76. { $set: { owner_ref: newOwnerId } }
  77. ).exec()
  78. await CollaboratorsHandler.promises.addUserIdToProject(
  79. projectId,
  80. newOwnerId,
  81. previousOwnerId,
  82. PrivilegeLevels.READ_AND_WRITE
  83. )
  84. }
  85. async function _sendEmails(project, previousOwner, newOwner) {
  86. if (previousOwner == null) {
  87. // The previous owner didn't exist. This is not supposed to happen, but
  88. // since we're changing the owner anyway, we'll just warn
  89. logger.warn(
  90. { projectId: project._id, ownerId: previousOwner._id },
  91. 'Project owner did not exist before ownership transfer'
  92. )
  93. } else {
  94. // Send confirmation emails
  95. await Promise.all([
  96. EmailHandler.promises.sendEmail(
  97. 'ownershipTransferConfirmationPreviousOwner',
  98. {
  99. to: previousOwner.email,
  100. project,
  101. newOwner
  102. }
  103. ),
  104. EmailHandler.promises.sendEmail('ownershipTransferConfirmationNewOwner', {
  105. to: newOwner.email,
  106. project,
  107. previousOwner
  108. })
  109. ])
  110. }
  111. }