PasswordResetHandler.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. const settings = require('@overleaf/settings')
  2. const UserAuditLogHandler = require('../User/UserAuditLogHandler')
  3. const UserGetter = require('../User/UserGetter')
  4. const OneTimeTokenHandler = require('../Security/OneTimeTokenHandler')
  5. const EmailHandler = require('../Email/EmailHandler')
  6. const AuthenticationManager = require('../Authentication/AuthenticationManager')
  7. const { callbackify, promisify } = require('util')
  8. const AUDIT_LOG_TOKEN_PREFIX_LENGTH = 10
  9. function generateAndEmailResetToken(email, callback) {
  10. UserGetter.getUserByAnyEmail(email, (err, user) => {
  11. if (err || !user) {
  12. return callback(err, null)
  13. }
  14. if (user.email !== email) {
  15. return callback(null, 'secondary')
  16. }
  17. const data = { user_id: user._id.toString(), email }
  18. OneTimeTokenHandler.getNewToken('password', data, (err, token) => {
  19. if (err) {
  20. return callback(err)
  21. }
  22. const emailOptions = {
  23. to: email,
  24. setNewPasswordUrl: `${
  25. settings.siteUrl
  26. }/user/password/set?passwordResetToken=${token}&email=${encodeURIComponent(
  27. email
  28. )}`,
  29. }
  30. EmailHandler.sendEmail('passwordResetRequested', emailOptions, err => {
  31. if (err) {
  32. return callback(err)
  33. }
  34. callback(null, 'primary')
  35. })
  36. })
  37. })
  38. }
  39. function expirePasswordResetToken(token, callback) {
  40. OneTimeTokenHandler.expireToken('password', token, err => {
  41. return callback(err)
  42. })
  43. }
  44. function getUserForPasswordResetToken(token, callback) {
  45. OneTimeTokenHandler.peekValueFromToken(
  46. 'password',
  47. token,
  48. (err, data, remainingUses) => {
  49. if (err != null) {
  50. if (err.name === 'NotFoundError') {
  51. return callback(null, null)
  52. } else {
  53. return callback(err)
  54. }
  55. }
  56. if (data == null || data.email == null) {
  57. return callback(null, null, remainingUses)
  58. }
  59. UserGetter.getUserByMainEmail(
  60. data.email,
  61. { _id: 1, 'overleaf.id': 1, email: 1 },
  62. (err, user) => {
  63. if (err != null) {
  64. callback(err)
  65. } else if (user == null) {
  66. callback(null, null, 0)
  67. } else if (
  68. data.user_id != null &&
  69. data.user_id === user._id.toString()
  70. ) {
  71. callback(null, user, remainingUses)
  72. } else if (
  73. data.v1_user_id != null &&
  74. user.overleaf != null &&
  75. data.v1_user_id === user.overleaf.id
  76. ) {
  77. callback(null, user, remainingUses)
  78. } else {
  79. callback(null, null, 0)
  80. }
  81. }
  82. )
  83. }
  84. )
  85. }
  86. async function setNewUserPassword(token, password, auditLog) {
  87. const user = await PasswordResetHandler.promises.getUserForPasswordResetToken(
  88. token
  89. )
  90. if (!user) {
  91. return {
  92. found: false,
  93. reset: false,
  94. userId: null,
  95. }
  96. }
  97. await UserAuditLogHandler.promises.addEntry(
  98. user._id,
  99. 'reset-password',
  100. auditLog.initiatorId,
  101. auditLog.ip,
  102. { token: token.substring(0, AUDIT_LOG_TOKEN_PREFIX_LENGTH) }
  103. )
  104. const reset = await AuthenticationManager.promises.setUserPassword(
  105. user,
  106. password
  107. )
  108. await PasswordResetHandler.promises.expirePasswordResetToken(token)
  109. return { found: true, reset, userId: user._id }
  110. }
  111. const PasswordResetHandler = {
  112. generateAndEmailResetToken,
  113. setNewUserPassword: callbackify(setNewUserPassword),
  114. getUserForPasswordResetToken,
  115. expirePasswordResetToken,
  116. }
  117. PasswordResetHandler.promises = {
  118. getUserForPasswordResetToken: promisify(
  119. PasswordResetHandler.getUserForPasswordResetToken
  120. ),
  121. expirePasswordResetToken: promisify(
  122. PasswordResetHandler.expirePasswordResetToken
  123. ),
  124. setNewUserPassword,
  125. }
  126. module.exports = PasswordResetHandler