PasswordResetController.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. const PasswordResetHandler = require('./PasswordResetHandler')
  2. const AuthenticationController = require('../Authentication/AuthenticationController')
  3. const AuthenticationManager = require('../Authentication/AuthenticationManager')
  4. const SessionManager = require('../Authentication/SessionManager')
  5. const UserGetter = require('../User/UserGetter')
  6. const UserUpdater = require('../User/UserUpdater')
  7. const UserSessionsManager = require('../User/UserSessionsManager')
  8. const OError = require('@overleaf/o-error')
  9. const EmailsHelper = require('../Helpers/EmailHelper')
  10. const { expressify } = require('../../util/promises')
  11. async function setNewUserPassword(req, res, next) {
  12. let user
  13. let { passwordResetToken, password, email } = req.body
  14. if (!passwordResetToken || !password) {
  15. return res.status(400).json({
  16. message: {
  17. key: 'invalid-password',
  18. },
  19. })
  20. }
  21. const err = AuthenticationManager.validatePassword(password, email)
  22. if (err) {
  23. if (err?.info?.code === 'contains_email') {
  24. return res.status(400).json({
  25. message: {
  26. text: req.i18n.translate('invalid_password_contains_email'),
  27. },
  28. })
  29. } else if (err?.info?.code === 'too_similar') {
  30. return res.status(400).json({
  31. message: {
  32. text: req.i18n.translate('invalid_password_too_similar'),
  33. },
  34. })
  35. } else {
  36. return res.status(400).json({
  37. message: { text: err.message },
  38. })
  39. }
  40. }
  41. passwordResetToken = passwordResetToken.trim()
  42. delete req.session.resetToken
  43. const initiatorId = SessionManager.getLoggedInUserId(req.session)
  44. // password reset via tokens can be done while logged in, or not
  45. const auditLog = {
  46. initiatorId,
  47. ip: req.ip,
  48. }
  49. try {
  50. const result = await PasswordResetHandler.promises.setNewUserPassword(
  51. passwordResetToken,
  52. password,
  53. auditLog
  54. )
  55. const { found, reset, userId } = result
  56. if (!found) {
  57. return res.status(404).json({
  58. message: {
  59. key: 'token-expired',
  60. },
  61. })
  62. }
  63. if (!reset) {
  64. return res.status(500).json({
  65. message: req.i18n.translate('error_performing_request'),
  66. })
  67. }
  68. await UserSessionsManager.promises.revokeAllUserSessions(
  69. { _id: userId },
  70. []
  71. )
  72. await UserUpdater.promises.removeReconfirmFlag(userId)
  73. if (!req.session.doLoginAfterPasswordReset) {
  74. return res.sendStatus(200)
  75. }
  76. user = await UserGetter.promises.getUser(userId)
  77. } catch (error) {
  78. if (error.name === 'NotFoundError') {
  79. return res.status(404).json({
  80. message: {
  81. key: 'token-expired',
  82. },
  83. })
  84. } else if (error.name === 'InvalidPasswordError') {
  85. return res.status(400).json({
  86. message: {
  87. key: 'invalid-password',
  88. },
  89. })
  90. } else if (error.name === 'PasswordMustBeDifferentError') {
  91. return res.status(400).json({
  92. message: {
  93. key: 'password-must-be-different',
  94. },
  95. })
  96. } else if (error.name === 'PasswordReusedError') {
  97. return res.status(400).json({
  98. message: {
  99. key: 'password-must-be-strong',
  100. },
  101. })
  102. } else {
  103. return res.status(500).json({
  104. message: req.i18n.translate('error_performing_request'),
  105. })
  106. }
  107. }
  108. AuthenticationController.setAuditInfo(req, {
  109. method: 'Password reset, set new password',
  110. })
  111. AuthenticationController.finishLogin(user, req, res, next)
  112. }
  113. module.exports = {
  114. renderRequestResetForm(req, res) {
  115. const errorQuery = req.query.error
  116. let error = null
  117. if (errorQuery === 'token_expired') {
  118. error = 'password_reset_token_expired'
  119. }
  120. res.render('user/passwordReset', {
  121. title: 'reset_password',
  122. error,
  123. })
  124. },
  125. requestReset(req, res, next) {
  126. const email = EmailsHelper.parseEmail(req.body.email)
  127. if (!email) {
  128. return res.status(400).json({
  129. message: req.i18n.translate('must_be_email_address'),
  130. })
  131. }
  132. PasswordResetHandler.generateAndEmailResetToken(email, (err, status) => {
  133. if (err != null) {
  134. OError.tag(err, 'failed to generate and email password reset token', {
  135. email,
  136. })
  137. next(err)
  138. } else if (status === 'primary') {
  139. res.status(200).json({
  140. message: req.i18n.translate('password_reset_email_sent'),
  141. })
  142. } else if (status === 'secondary') {
  143. res.status(404).json({
  144. message: req.i18n.translate('secondary_email_password_reset'),
  145. })
  146. } else {
  147. res.status(404).json({
  148. message: req.i18n.translate('cant_find_email'),
  149. })
  150. }
  151. })
  152. },
  153. renderSetPasswordForm(req, res) {
  154. if (req.query.passwordResetToken != null) {
  155. return PasswordResetHandler.getUserForPasswordResetToken(
  156. req.query.passwordResetToken,
  157. (err, user, remainingUses) => {
  158. if (err || !user || remainingUses <= 0) {
  159. return res.redirect('/user/password/reset?error=token_expired')
  160. }
  161. req.session.resetToken = req.query.passwordResetToken
  162. let emailQuery = ''
  163. if (typeof req.query.email === 'string') {
  164. const email = EmailsHelper.parseEmail(req.query.email)
  165. if (email) {
  166. emailQuery = `?email=${encodeURIComponent(email)}`
  167. }
  168. }
  169. return res.redirect('/user/password/set' + emailQuery)
  170. }
  171. )
  172. }
  173. if (req.session.resetToken == null) {
  174. return res.redirect('/user/password/reset')
  175. }
  176. const email = EmailsHelper.parseEmail(req.query.email)
  177. res.render('user/setPassword', {
  178. title: 'set_password',
  179. email,
  180. passwordResetToken: req.session.resetToken,
  181. })
  182. },
  183. setNewUserPassword: expressify(setNewUserPassword),
  184. }