| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- import settings from '@overleaf/settings'
- import UserAuditLogHandler from '../User/UserAuditLogHandler.js'
- import UserGetter from '../User/UserGetter.js'
- import OneTimeTokenHandler from '../Security/OneTimeTokenHandler.js'
- import EmailHandler from '../Email/EmailHandler.js'
- import AuthenticationManager from '../Authentication/AuthenticationManager.js'
- import { callbackify, promisify } from 'node:util'
- import PermissionsManager from '../Authorization/PermissionsManager.js'
- const assertUserPermissions = PermissionsManager.promises.assertUserPermissions
- const AUDIT_LOG_TOKEN_PREFIX_LENGTH = 10
- async function generateAndEmailResetToken(email) {
- const user = await UserGetter.promises.getUserByAnyEmail(email)
- if (!user) {
- return null
- }
- if (user.email !== email) {
- return 'secondary'
- }
- await assertUserPermissions(user, ['change-password'])
- const data = { user_id: user._id.toString(), email }
- const token = await OneTimeTokenHandler.promises.getNewToken('password', data)
- const emailOptions = {
- to: email,
- setNewPasswordUrl: `${
- settings.siteUrl
- }/user/password/set?passwordResetToken=${token}&email=${encodeURIComponent(
- email
- )}`,
- }
- await EmailHandler.promises.sendEmail('passwordResetRequested', emailOptions)
- return 'primary'
- }
- function expirePasswordResetToken(token, callback) {
- OneTimeTokenHandler.expireToken('password', token, err => {
- return callback(err)
- })
- }
- async function getUserForPasswordResetToken(token) {
- let result
- try {
- result = await OneTimeTokenHandler.promises.peekValueFromToken(
- 'password',
- token
- )
- } catch (err) {
- if (err.name === 'NotFoundError') {
- return
- } else {
- throw err
- }
- }
- const { data, remainingPeeks } = result || {}
- if (data == null || data.email == null) {
- return { user: null, remainingPeeks }
- }
- const user = await UserGetter.promises.getUserByMainEmail(data.email, {
- _id: 1,
- 'overleaf.id': 1,
- email: 1,
- })
- await assertUserPermissions(user, ['change-password'])
- if (user == null) {
- return { user: null, remainingPeeks: 0 }
- } else if (data.user_id != null && data.user_id === user._id.toString()) {
- return { user, remainingPeeks }
- } else if (
- data.v1_user_id != null &&
- user.overleaf != null &&
- data.v1_user_id === user.overleaf.id
- ) {
- return { user, remainingPeeks }
- } else {
- return { user: null, remainingPeeks: 0 }
- }
- }
- async function setNewUserPassword(token, password, auditLog) {
- const result =
- await PasswordResetHandler.promises.getUserForPasswordResetToken(token)
- const { user } = result || {}
- if (!user) {
- return {
- found: false,
- reset: false,
- userId: null,
- }
- }
- await UserAuditLogHandler.promises.addEntry(
- user._id,
- 'reset-password',
- auditLog.initiatorId,
- auditLog.ip,
- { token: token.substring(0, AUDIT_LOG_TOKEN_PREFIX_LENGTH) }
- )
- const reset = await AuthenticationManager.promises.setUserPassword(
- user,
- password
- )
- await PasswordResetHandler.promises.expirePasswordResetToken(token)
- return { found: true, reset, userId: user._id }
- }
- const PasswordResetHandler = {
- generateAndEmailResetToken: callbackify(generateAndEmailResetToken),
- setNewUserPassword: callbackify(setNewUserPassword),
- getUserForPasswordResetToken: callbackify(getUserForPasswordResetToken),
- expirePasswordResetToken,
- }
- PasswordResetHandler.promises = {
- generateAndEmailResetToken,
- getUserForPasswordResetToken,
- expirePasswordResetToken: promisify(
- PasswordResetHandler.expirePasswordResetToken
- ),
- setNewUserPassword,
- }
- export default PasswordResetHandler
|