UserRegistrationHandler.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { User } from '../../models/User.js'
  2. import UserCreator from './UserCreator.mjs'
  3. import UserGetter from './UserGetter.js'
  4. import AuthenticationManager from '../Authentication/AuthenticationManager.js'
  5. import NewsletterManager from '../Newsletter/NewsletterManager.js'
  6. import logger from '@overleaf/logger'
  7. import crypto from 'node:crypto'
  8. import EmailHandler from '../Email/EmailHandler.js'
  9. import OneTimeTokenHandler from '../Security/OneTimeTokenHandler.js'
  10. import settings from '@overleaf/settings'
  11. import EmailHelper from '../Helpers/EmailHelper.js'
  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. NewsletterManager.subscribe(user)
  67. } catch (error) {
  68. logger.warn(
  69. { err: error, user },
  70. 'Failed to subscribe user to newsletter'
  71. )
  72. throw error
  73. }
  74. }
  75. return user
  76. },
  77. async registerNewUserAndSendActivationEmail(email) {
  78. let user
  79. try {
  80. user = await UserRegistrationHandler.registerNewUser({
  81. email,
  82. password: crypto.randomBytes(32).toString('hex'),
  83. })
  84. } catch (error) {
  85. if (error.message === 'EmailAlreadyRegistered') {
  86. logger.debug({ email }, 'user already exists, resending welcome email')
  87. user = await UserGetter.promises.getUserByAnyEmail(email)
  88. } else {
  89. throw error
  90. }
  91. }
  92. const ONE_WEEK = 7 * 24 * 60 * 60 // seconds
  93. const token = await OneTimeTokenHandler.promises.getNewToken(
  94. 'password',
  95. { user_id: user._id.toString(), email: user.email },
  96. { expiresIn: ONE_WEEK }
  97. )
  98. const setNewPasswordUrl = `${settings.siteUrl}/user/activate?token=${token}&user_id=${user._id}`
  99. await EmailHandler.promises
  100. .sendEmail('registered', {
  101. to: user.email,
  102. setNewPasswordUrl,
  103. })
  104. .catch(error => {
  105. logger.warn({ err: error }, 'failed to send activation email')
  106. })
  107. return { user, setNewPasswordUrl }
  108. },
  109. }
  110. export default {
  111. registerNewUser: callbackify(UserRegistrationHandler.registerNewUser),
  112. registerNewUserAndSendActivationEmail: callbackifyMultiResult(
  113. UserRegistrationHandler.registerNewUserAndSendActivationEmail,
  114. ['user', 'setNewPasswordUrl']
  115. ),
  116. promises: UserRegistrationHandler,
  117. }