UserEmailsController.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. const logger = require('@overleaf/logger')
  2. const SessionManager = require('../Authentication/SessionManager')
  3. const UserGetter = require('./UserGetter')
  4. const UserUpdater = require('./UserUpdater')
  5. const UserSessionsManager = require('./UserSessionsManager')
  6. const EmailHandler = require('../Email/EmailHandler')
  7. const EmailHelper = require('../Helpers/EmailHelper')
  8. const UserEmailsConfirmationHandler = require('./UserEmailsConfirmationHandler')
  9. const { endorseAffiliation } = require('../Institutions/InstitutionsAPI')
  10. const Errors = require('../Errors/Errors')
  11. const HttpErrorHandler = require('../Errors/HttpErrorHandler')
  12. const { expressify } = require('../../util/promises')
  13. const AsyncFormHelper = require('../Helpers/AsyncFormHelper')
  14. const AnalyticsManager = require('../Analytics/AnalyticsManager')
  15. const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')
  16. async function _sendSecurityAlertEmail(user, email) {
  17. const emailOptions = {
  18. to: user.email,
  19. actionDescribed: `a secondary email address has been added to your account ${user.email}`,
  20. message: [
  21. `<span style="display:inline-block;padding: 0 20px;width:100%;">Added: <br/><b>${email}</b></span>`,
  22. ],
  23. action: 'secondary email address added',
  24. }
  25. await EmailHandler.promises.sendEmail('securityAlert', emailOptions)
  26. }
  27. async function add(req, res, next) {
  28. const userId = SessionManager.getLoggedInUserId(req.session)
  29. const email = EmailHelper.parseEmail(req.body.email)
  30. if (!email) {
  31. return res.sendStatus(422)
  32. }
  33. const user = await UserGetter.promises.getUser(userId, { email: 1 })
  34. const affiliationOptions = {
  35. university: req.body.university,
  36. role: req.body.role,
  37. department: req.body.department,
  38. }
  39. try {
  40. await UserUpdater.promises.addEmailAddress(
  41. userId,
  42. email,
  43. affiliationOptions,
  44. {
  45. initiatorId: user._id,
  46. ipAddress: req.ip,
  47. }
  48. )
  49. } catch (error) {
  50. return UserEmailsController._handleEmailError(error, req, res, next)
  51. }
  52. await _sendSecurityAlertEmail(user, email)
  53. await UserEmailsConfirmationHandler.promises.sendConfirmationEmail(
  54. userId,
  55. email
  56. )
  57. res.sendStatus(204)
  58. }
  59. function resendConfirmation(req, res, next) {
  60. const userId = SessionManager.getLoggedInUserId(req.session)
  61. const email = EmailHelper.parseEmail(req.body.email)
  62. if (!email) {
  63. return res.sendStatus(422)
  64. }
  65. UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (error, user) {
  66. if (error) {
  67. return next(error)
  68. }
  69. if (!user || user._id.toString() !== userId) {
  70. return res.sendStatus(422)
  71. }
  72. UserEmailsConfirmationHandler.sendConfirmationEmail(
  73. userId,
  74. email,
  75. function (error) {
  76. if (error) {
  77. return next(error)
  78. }
  79. res.sendStatus(200)
  80. }
  81. )
  82. })
  83. }
  84. function sendReconfirmation(req, res, next) {
  85. const userId = SessionManager.getLoggedInUserId(req.session)
  86. const email = EmailHelper.parseEmail(req.body.email)
  87. if (!email) {
  88. return res.sendStatus(400)
  89. }
  90. UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (error, user) {
  91. if (error) {
  92. return next(error)
  93. }
  94. if (!user || user._id.toString() !== userId) {
  95. return res.sendStatus(422)
  96. }
  97. UserEmailsConfirmationHandler.sendReconfirmationEmail(
  98. userId,
  99. email,
  100. function (error) {
  101. if (error) {
  102. return next(error)
  103. }
  104. res.sendStatus(204)
  105. }
  106. )
  107. })
  108. }
  109. async function primaryEmailCheckPage(req, res) {
  110. const userId = SessionManager.getLoggedInUserId(req.session)
  111. const user = await UserGetter.promises.getUser(userId, {
  112. lastPrimaryEmailCheck: 1,
  113. signUpDate: 1,
  114. email: 1,
  115. emails: 1,
  116. })
  117. if (!UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)) {
  118. return res.redirect('/project')
  119. }
  120. AnalyticsManager.recordEventForUser(
  121. userId,
  122. 'primary-email-check-page-displayed'
  123. )
  124. res.render('user/primaryEmailCheck')
  125. }
  126. async function primaryEmailCheck(req, res) {
  127. const userId = SessionManager.getLoggedInUserId(req.session)
  128. await UserUpdater.promises.updateUser(userId, {
  129. $set: { lastPrimaryEmailCheck: new Date() },
  130. })
  131. AnalyticsManager.recordEventForUser(userId, 'primary-email-check-done')
  132. AsyncFormHelper.redirect(req, res, '/project')
  133. }
  134. const UserEmailsController = {
  135. list(req, res, next) {
  136. const userId = SessionManager.getLoggedInUserId(req.session)
  137. UserGetter.getUserFullEmails(userId, function (error, fullEmails) {
  138. if (error) {
  139. return next(error)
  140. }
  141. res.json(fullEmails)
  142. })
  143. },
  144. add: expressify(add),
  145. remove(req, res, next) {
  146. const userId = SessionManager.getLoggedInUserId(req.session)
  147. const email = EmailHelper.parseEmail(req.body.email)
  148. if (!email) {
  149. return res.sendStatus(422)
  150. }
  151. const auditLog = {
  152. initiatorId: userId,
  153. ipAddress: req.ip,
  154. }
  155. UserUpdater.removeEmailAddress(userId, email, auditLog, function (error) {
  156. if (error) {
  157. return next(error)
  158. }
  159. res.sendStatus(200)
  160. })
  161. },
  162. setDefault(req, res, next) {
  163. const userId = SessionManager.getLoggedInUserId(req.session)
  164. const email = EmailHelper.parseEmail(req.body.email)
  165. if (!email) {
  166. return res.sendStatus(422)
  167. }
  168. const auditLog = {
  169. initiatorId: userId,
  170. ipAddress: req.ip,
  171. }
  172. UserUpdater.setDefaultEmailAddress(
  173. userId,
  174. email,
  175. false,
  176. auditLog,
  177. true,
  178. err => {
  179. if (err) {
  180. return UserEmailsController._handleEmailError(err, req, res, next)
  181. }
  182. SessionManager.setInSessionUser(req.session, { email })
  183. const user = SessionManager.getSessionUser(req.session)
  184. UserSessionsManager.revokeAllUserSessions(
  185. user,
  186. [req.sessionID],
  187. err => {
  188. if (err)
  189. logger.warn(
  190. { err },
  191. 'failed revoking secondary sessions after changing default email'
  192. )
  193. }
  194. )
  195. res.sendStatus(200)
  196. }
  197. )
  198. },
  199. endorse(req, res, next) {
  200. const userId = SessionManager.getLoggedInUserId(req.session)
  201. const email = EmailHelper.parseEmail(req.body.email)
  202. if (!email) {
  203. return res.sendStatus(422)
  204. }
  205. endorseAffiliation(
  206. userId,
  207. email,
  208. req.body.role,
  209. req.body.department,
  210. function (error) {
  211. if (error) {
  212. return next(error)
  213. }
  214. res.sendStatus(204)
  215. }
  216. )
  217. },
  218. resendConfirmation,
  219. sendReconfirmation,
  220. primaryEmailCheckPage: expressify(primaryEmailCheckPage),
  221. primaryEmailCheck: expressify(primaryEmailCheck),
  222. showConfirm(req, res, next) {
  223. res.render('user/confirm_email', {
  224. token: req.query.token,
  225. title: 'confirm_email',
  226. })
  227. },
  228. confirm(req, res, next) {
  229. const { token } = req.body
  230. if (!token) {
  231. return res.status(422).json({
  232. message: req.i18n.translate('confirmation_link_broken'),
  233. })
  234. }
  235. UserEmailsConfirmationHandler.confirmEmailFromToken(
  236. token,
  237. function (error) {
  238. if (error) {
  239. if (error instanceof Errors.NotFoundError) {
  240. res.status(404).json({
  241. message: req.i18n.translate('confirmation_token_invalid'),
  242. })
  243. } else {
  244. next(error)
  245. }
  246. } else {
  247. res.sendStatus(200)
  248. }
  249. }
  250. )
  251. },
  252. _handleEmailError(error, req, res, next) {
  253. if (error instanceof Errors.UnconfirmedEmailError) {
  254. return HttpErrorHandler.conflict(req, res, 'email must be confirmed')
  255. } else if (error instanceof Errors.EmailExistsError) {
  256. const message = req.i18n.translate('email_already_registered')
  257. return HttpErrorHandler.conflict(req, res, message)
  258. } else if (error.message === '422: Email does not belong to university') {
  259. const message = req.i18n.translate('email_does_not_belong_to_university')
  260. return HttpErrorHandler.conflict(req, res, message)
  261. }
  262. next(error)
  263. },
  264. }
  265. module.exports = UserEmailsController