UserRegistrationHandler.mjs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { User } from '../../models/User.mjs'
  2. import UserCreator from './UserCreator.mjs'
  3. import UserGetter from './UserGetter.mjs'
  4. import AuthenticationManager from '../Authentication/AuthenticationManager.mjs'
  5. import Modules from '../../infrastructure/Modules.mjs'
  6. import logger from '@overleaf/logger'
  7. import crypto from 'node:crypto'
  8. import EmailHandler from '../Email/EmailHandler.mjs'
  9. import OneTimeTokenHandler from '../Security/OneTimeTokenHandler.mjs'
  10. import settings from '@overleaf/settings'
  11. import EmailHelper from '../Helpers/EmailHelper.mjs'
  12. import { callbackify, callbackifyMultiResult } from '@overleaf/promise-utils'
  13. import OError from '@overleaf/o-error'
  14. const UserRegistrationHandler = {
  15. _registrationRequestIsValid(body) {
  16. const invalidEmail = AuthenticationManager.validateEmail(body.email || '')
  17. const invalidPassword = AuthenticationManager.validatePassword(
  18. body.password || '',
  19. body.email
  20. )
  21. return !(invalidEmail || invalidPassword)
  22. },
  23. async _createNewUserIfRequired(user, userDetails) {
  24. if (!user) {
  25. userDetails.holdingAccount = false
  26. return await UserCreator.promises.createNewUser(
  27. {
  28. holdingAccount: false,
  29. email: userDetails.email,
  30. first_name: userDetails.first_name,
  31. last_name: userDetails.last_name,
  32. analyticsId: userDetails.analyticsId,
  33. },
  34. {}
  35. )
  36. }
  37. return user
  38. },
  39. async registerNewUser(userDetails) {
  40. const requestIsValid =
  41. UserRegistrationHandler._registrationRequestIsValid(userDetails)
  42. if (!requestIsValid) {
  43. throw new Error('request is not valid')
  44. }
  45. userDetails.email = EmailHelper.parseEmail(userDetails.email)
  46. let user = await UserGetter.promises.getUserByAnyEmail(userDetails.email)
  47. if (user && user.holdingAccount === false) {
  48. // We add userId to the error object so that the calling function can access
  49. // the id of the already existing user account.
  50. throw new OError('EmailAlreadyRegistered', { userId: user._id })
  51. }
  52. user = await UserRegistrationHandler._createNewUserIfRequired(
  53. user,
  54. userDetails
  55. )
  56. await User.updateOne(
  57. { _id: user._id },
  58. { $set: { holdingAccount: false } }
  59. ).exec()
  60. await AuthenticationManager.promises.setUserPassword(
  61. user,
  62. userDetails.password
  63. )
  64. if (userDetails.subscribeToNewsletter === 'true') {
  65. try {
  66. await Modules.promises.hooks.fire(
  67. 'updateTopicSubscription',
  68. user._id,
  69. 'newsletter',
  70. true
  71. )
  72. } catch (error) {
  73. logger.warn(
  74. { err: error, user },
  75. 'Failed to subscribe user to newsletter'
  76. )
  77. throw error
  78. }
  79. }
  80. return user
  81. },
  82. async registerNewUserAndSendActivationEmail(email) {
  83. let user
  84. try {
  85. user = await UserRegistrationHandler.registerNewUser({
  86. email,
  87. password: crypto.randomBytes(32).toString('hex'),
  88. })
  89. } catch (error) {
  90. if (error.message === 'EmailAlreadyRegistered') {
  91. logger.debug({ email }, 'user already exists, resending welcome email')
  92. user = await UserGetter.promises.getUserByAnyEmail(email)
  93. } else {
  94. throw error
  95. }
  96. }
  97. const ONE_WEEK = 7 * 24 * 60 * 60 // seconds
  98. const token = await OneTimeTokenHandler.promises.getNewToken(
  99. 'password',
  100. { user_id: user._id.toString(), email: user.email },
  101. { expiresIn: ONE_WEEK }
  102. )
  103. const setNewPasswordUrl = `${settings.siteUrl}/user/activate?token=${token}&user_id=${user._id}`
  104. await EmailHandler.promises
  105. .sendEmail('registered', {
  106. to: user.email,
  107. setNewPasswordUrl,
  108. })
  109. .catch(error => {
  110. logger.warn({ err: error }, 'failed to send activation email')
  111. })
  112. return { user, setNewPasswordUrl }
  113. },
  114. }
  115. export default {
  116. registerNewUser: callbackify(UserRegistrationHandler.registerNewUser),
  117. registerNewUserAndSendActivationEmail: callbackifyMultiResult(
  118. UserRegistrationHandler.registerNewUserAndSendActivationEmail,
  119. ['user', 'setNewPasswordUrl']
  120. ),
  121. promises: UserRegistrationHandler,
  122. }