2023-05-26 06:56:15 -04:00
|
|
|
const Settings = require('@overleaf/settings')
|
2021-11-10 08:40:18 -05:00
|
|
|
const logger = require('@overleaf/logger')
|
2021-07-28 04:51:20 -04:00
|
|
|
const SessionManager = require('../Authentication/SessionManager')
|
2019-05-29 05:21:06 -04:00
|
|
|
const UserGetter = require('./UserGetter')
|
|
|
|
const UserUpdater = require('./UserUpdater')
|
2020-10-30 08:33:13 -04:00
|
|
|
const UserSessionsManager = require('./UserSessionsManager')
|
2020-07-27 10:34:07 -04:00
|
|
|
const EmailHandler = require('../Email/EmailHandler')
|
2019-05-29 05:21:06 -04:00
|
|
|
const EmailHelper = require('../Helpers/EmailHelper')
|
|
|
|
const UserEmailsConfirmationHandler = require('./UserEmailsConfirmationHandler')
|
|
|
|
const { endorseAffiliation } = require('../Institutions/InstitutionsAPI')
|
|
|
|
const Errors = require('../Errors/Errors')
|
2020-07-20 09:21:28 -04:00
|
|
|
const HttpErrorHandler = require('../Errors/HttpErrorHandler')
|
2020-07-27 10:34:07 -04:00
|
|
|
const { expressify } = require('../../util/promises')
|
2022-02-03 06:37:34 -05:00
|
|
|
const AsyncFormHelper = require('../Helpers/AsyncFormHelper')
|
|
|
|
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
|
|
|
const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2020-09-14 09:54:19 -04:00
|
|
|
async function _sendSecurityAlertEmail(user, email) {
|
|
|
|
const emailOptions = {
|
|
|
|
to: user.email,
|
2020-12-15 05:23:54 -05:00
|
|
|
actionDescribed: `a secondary email address has been added to your account ${user.email}`,
|
2020-09-14 09:54:19 -04:00
|
|
|
message: [
|
2021-04-27 03:52:58 -04:00
|
|
|
`<span style="display:inline-block;padding: 0 20px;width:100%;">Added: <br/><b>${email}</b></span>`,
|
2020-09-14 09:54:19 -04:00
|
|
|
],
|
2021-04-27 03:52:58 -04:00
|
|
|
action: 'secondary email address added',
|
2020-09-14 09:54:19 -04:00
|
|
|
}
|
|
|
|
await EmailHandler.promises.sendEmail('securityAlert', emailOptions)
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:34:07 -04:00
|
|
|
async function add(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2019-10-04 13:42:07 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
|
|
|
if (!email) {
|
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
2023-05-26 06:56:15 -04:00
|
|
|
const user = await UserGetter.promises.getUser(userId, {
|
|
|
|
email: 1,
|
|
|
|
'emails.email': 1,
|
|
|
|
})
|
|
|
|
|
|
|
|
if (user.emails.length >= Settings.emailAddressLimit) {
|
|
|
|
return res.status(422).json({ message: 'secondary email limit exceeded' })
|
|
|
|
}
|
2019-10-04 13:42:07 -04:00
|
|
|
|
|
|
|
const affiliationOptions = {
|
|
|
|
university: req.body.university,
|
|
|
|
role: req.body.role,
|
2021-04-27 03:52:58 -04:00
|
|
|
department: req.body.department,
|
2019-10-04 13:42:07 -04:00
|
|
|
}
|
2020-07-27 10:34:07 -04:00
|
|
|
|
|
|
|
try {
|
|
|
|
await UserUpdater.promises.addEmailAddress(
|
|
|
|
userId,
|
|
|
|
email,
|
2020-09-14 09:54:19 -04:00
|
|
|
affiliationOptions,
|
|
|
|
{
|
|
|
|
initiatorId: user._id,
|
2021-04-27 03:52:58 -04:00
|
|
|
ipAddress: req.ip,
|
2020-09-14 09:54:19 -04:00
|
|
|
}
|
2020-07-27 10:34:07 -04:00
|
|
|
)
|
|
|
|
} catch (error) {
|
|
|
|
return UserEmailsController._handleEmailError(error, req, res, next)
|
|
|
|
}
|
|
|
|
|
2020-09-14 09:54:19 -04:00
|
|
|
await _sendSecurityAlertEmail(user, email)
|
|
|
|
|
2020-07-27 10:34:07 -04:00
|
|
|
await UserEmailsConfirmationHandler.promises.sendConfirmationEmail(
|
|
|
|
userId,
|
|
|
|
email
|
|
|
|
)
|
|
|
|
|
|
|
|
res.sendStatus(204)
|
2019-10-04 13:42:07 -04:00
|
|
|
}
|
|
|
|
|
2019-10-24 08:48:37 -04:00
|
|
|
function resendConfirmation(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2019-10-14 09:18:44 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
|
|
|
if (!email) {
|
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (error, user) {
|
2019-10-14 09:18:44 -04:00
|
|
|
if (error) {
|
|
|
|
return next(error)
|
|
|
|
}
|
|
|
|
if (!user || user._id.toString() !== userId) {
|
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
UserEmailsConfirmationHandler.sendConfirmationEmail(
|
|
|
|
userId,
|
|
|
|
email,
|
|
|
|
function (error) {
|
|
|
|
if (error) {
|
|
|
|
return next(error)
|
|
|
|
}
|
|
|
|
res.sendStatus(200)
|
2019-10-14 09:18:44 -04:00
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
)
|
2019-10-14 09:18:44 -04:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-04-15 10:23:41 -04:00
|
|
|
function sendReconfirmation(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2021-04-15 10:23:41 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
|
|
|
if (!email) {
|
|
|
|
return res.sendStatus(400)
|
|
|
|
}
|
|
|
|
UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (error, user) {
|
|
|
|
if (error) {
|
|
|
|
return next(error)
|
|
|
|
}
|
|
|
|
if (!user || user._id.toString() !== userId) {
|
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
|
|
|
UserEmailsConfirmationHandler.sendReconfirmationEmail(
|
|
|
|
userId,
|
|
|
|
email,
|
|
|
|
function (error) {
|
|
|
|
if (error) {
|
|
|
|
return next(error)
|
|
|
|
}
|
2021-09-21 09:47:09 -04:00
|
|
|
res.sendStatus(204)
|
2021-04-15 10:23:41 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-02-03 06:37:34 -05:00
|
|
|
async function primaryEmailCheckPage(req, res) {
|
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
|
|
|
const user = await UserGetter.promises.getUser(userId, {
|
|
|
|
lastPrimaryEmailCheck: 1,
|
|
|
|
signUpDate: 1,
|
|
|
|
email: 1,
|
|
|
|
emails: 1,
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)) {
|
|
|
|
return res.redirect('/project')
|
|
|
|
}
|
|
|
|
|
|
|
|
AnalyticsManager.recordEventForUser(
|
|
|
|
userId,
|
|
|
|
'primary-email-check-page-displayed'
|
|
|
|
)
|
|
|
|
res.render('user/primaryEmailCheck')
|
|
|
|
}
|
|
|
|
|
|
|
|
async function primaryEmailCheck(req, res) {
|
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
|
|
|
await UserUpdater.promises.updateUser(userId, {
|
|
|
|
$set: { lastPrimaryEmailCheck: new Date() },
|
|
|
|
})
|
|
|
|
AnalyticsManager.recordEventForUser(userId, 'primary-email-check-done')
|
|
|
|
AsyncFormHelper.redirect(req, res, '/project')
|
|
|
|
}
|
|
|
|
|
2020-07-27 10:34:07 -04:00
|
|
|
const UserEmailsController = {
|
2019-05-29 05:21:06 -04:00
|
|
|
list(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2021-04-14 09:17:21 -04:00
|
|
|
UserGetter.getUserFullEmails(userId, function (error, fullEmails) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (error) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return next(error)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
res.json(fullEmails)
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
2020-07-27 10:34:07 -04:00
|
|
|
add: expressify(add),
|
2019-05-29 05:21:06 -04:00
|
|
|
|
|
|
|
remove(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2019-05-29 05:21:06 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!email) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
2022-07-27 04:25:50 -04:00
|
|
|
const auditLog = {
|
|
|
|
initiatorId: userId,
|
|
|
|
ipAddress: req.ip,
|
|
|
|
}
|
|
|
|
UserUpdater.removeEmailAddress(userId, email, auditLog, function (error) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (error) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return next(error)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
res.sendStatus(200)
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
setDefault(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2019-05-29 05:21:06 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!email) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
2020-08-12 10:19:33 -04:00
|
|
|
const auditLog = {
|
|
|
|
initiatorId: userId,
|
2021-04-27 03:52:58 -04:00
|
|
|
ipAddress: req.ip,
|
2020-08-12 10:19:33 -04:00
|
|
|
}
|
2020-08-12 10:19:55 -04:00
|
|
|
UserUpdater.setDefaultEmailAddress(
|
|
|
|
userId,
|
|
|
|
email,
|
|
|
|
false,
|
|
|
|
auditLog,
|
|
|
|
true,
|
|
|
|
err => {
|
|
|
|
if (err) {
|
|
|
|
return UserEmailsController._handleEmailError(err, req, res, next)
|
|
|
|
}
|
2022-05-16 10:25:49 -04:00
|
|
|
SessionManager.setInSessionUser(req.session, { email })
|
2021-07-28 04:51:20 -04:00
|
|
|
const user = SessionManager.getSessionUser(req.session)
|
2020-10-30 08:33:13 -04:00
|
|
|
UserSessionsManager.revokeAllUserSessions(
|
|
|
|
user,
|
|
|
|
[req.sessionID],
|
|
|
|
err => {
|
|
|
|
if (err)
|
|
|
|
logger.warn(
|
|
|
|
{ err },
|
|
|
|
'failed revoking secondary sessions after changing default email'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
2020-08-12 10:19:55 -04:00
|
|
|
res.sendStatus(200)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2020-08-12 10:19:55 -04:00
|
|
|
)
|
2019-05-29 05:21:06 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
endorse(req, res, next) {
|
2021-07-28 04:51:20 -04:00
|
|
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
2019-05-29 05:21:06 -04:00
|
|
|
const email = EmailHelper.parseEmail(req.body.email)
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!email) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return res.sendStatus(422)
|
|
|
|
}
|
|
|
|
|
2019-09-24 04:43:43 -04:00
|
|
|
endorseAffiliation(
|
2019-05-29 05:21:06 -04:00
|
|
|
userId,
|
|
|
|
email,
|
|
|
|
req.body.role,
|
|
|
|
req.body.department,
|
2021-04-14 09:17:21 -04:00
|
|
|
function (error) {
|
2019-09-24 04:43:43 -04:00
|
|
|
if (error) {
|
2019-05-29 05:21:06 -04:00
|
|
|
return next(error)
|
|
|
|
}
|
2019-09-24 04:43:43 -04:00
|
|
|
res.sendStatus(204)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
|
2019-10-14 09:18:44 -04:00
|
|
|
resendConfirmation,
|
2019-05-29 05:21:06 -04:00
|
|
|
|
2021-04-15 10:23:41 -04:00
|
|
|
sendReconfirmation,
|
|
|
|
|
2022-02-03 06:37:34 -05:00
|
|
|
primaryEmailCheckPage: expressify(primaryEmailCheckPage),
|
|
|
|
|
|
|
|
primaryEmailCheck: expressify(primaryEmailCheck),
|
|
|
|
|
2019-05-29 05:21:06 -04:00
|
|
|
showConfirm(req, res, next) {
|
2019-09-24 04:43:43 -04:00
|
|
|
res.render('user/confirm_email', {
|
2019-05-29 05:21:06 -04:00
|
|
|
token: req.query.token,
|
2021-04-27 03:52:58 -04:00
|
|
|
title: 'confirm_email',
|
2019-05-29 05:21:06 -04:00
|
|
|
})
|
|
|
|
},
|
|
|
|
|
|
|
|
confirm(req, res, next) {
|
|
|
|
const { token } = req.body
|
2019-09-24 04:43:43 -04:00
|
|
|
if (!token) {
|
2020-01-13 11:59:58 -05:00
|
|
|
return res.status(422).json({
|
2021-04-27 03:52:58 -04:00
|
|
|
message: req.i18n.translate('confirmation_link_broken'),
|
2020-01-13 11:59:58 -05:00
|
|
|
})
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
UserEmailsConfirmationHandler.confirmEmailFromToken(
|
|
|
|
token,
|
|
|
|
function (error) {
|
|
|
|
if (error) {
|
|
|
|
if (error instanceof Errors.NotFoundError) {
|
|
|
|
res.status(404).json({
|
2021-04-27 03:52:58 -04:00
|
|
|
message: req.i18n.translate('confirmation_token_invalid'),
|
2021-04-14 09:17:21 -04:00
|
|
|
})
|
|
|
|
} else {
|
|
|
|
next(error)
|
|
|
|
}
|
2019-05-29 05:21:06 -04:00
|
|
|
} else {
|
2021-04-14 09:17:21 -04:00
|
|
|
res.sendStatus(200)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
|
|
|
}
|
2021-04-14 09:17:21 -04:00
|
|
|
)
|
2019-05-29 05:21:06 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
_handleEmailError(error, req, res, next) {
|
|
|
|
if (error instanceof Errors.UnconfirmedEmailError) {
|
2020-07-20 09:21:28 -04:00
|
|
|
return HttpErrorHandler.conflict(req, res, 'email must be confirmed')
|
2019-05-29 05:21:06 -04:00
|
|
|
} else if (error instanceof Errors.EmailExistsError) {
|
2020-07-20 09:21:28 -04:00
|
|
|
const message = req.i18n.translate('email_already_registered')
|
|
|
|
return HttpErrorHandler.conflict(req, res, message)
|
2020-04-08 09:43:15 -04:00
|
|
|
} else if (error.message === '422: Email does not belong to university') {
|
2020-07-20 09:21:28 -04:00
|
|
|
const message = req.i18n.translate('email_does_not_belong_to_university')
|
|
|
|
return HttpErrorHandler.conflict(req, res, message)
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2020-07-22 10:08:06 -04:00
|
|
|
next(error)
|
2021-04-27 03:52:58 -04:00
|
|
|
},
|
2019-05-29 05:21:06 -04:00
|
|
|
}
|
2020-07-27 10:34:07 -04:00
|
|
|
|
|
|
|
module.exports = UserEmailsController
|