UserEmailsController.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. let UserEmailsController
  2. const AuthenticationController = require('../Authentication/AuthenticationController')
  3. const Features = require('../../infrastructure/Features')
  4. const InstitutionsAPI = require('../Institutions/InstitutionsAPI')
  5. const UserGetter = require('./UserGetter')
  6. const UserUpdater = require('./UserUpdater')
  7. const EmailHelper = require('../Helpers/EmailHelper')
  8. const UserEmailsConfirmationHandler = require('./UserEmailsConfirmationHandler')
  9. const { endorseAffiliation } = require('../Institutions/InstitutionsAPI')
  10. const logger = require('logger-sharelatex')
  11. const Errors = require('../Errors/Errors')
  12. const HttpErrors = require('@overleaf/o-error/http')
  13. function add(req, res, next) {
  14. const userId = AuthenticationController.getLoggedInUserId(req)
  15. const email = EmailHelper.parseEmail(req.body.email)
  16. if (!email) {
  17. return res.sendStatus(422)
  18. }
  19. const affiliationOptions = {
  20. university: req.body.university,
  21. role: req.body.role,
  22. department: req.body.department
  23. }
  24. UserUpdater.addEmailAddress(userId, email, affiliationOptions, function(
  25. error
  26. ) {
  27. if (error) {
  28. return UserEmailsController._handleEmailError(error, req, res, next)
  29. }
  30. UserEmailsConfirmationHandler.sendConfirmationEmail(userId, email, function(
  31. error
  32. ) {
  33. if (error) {
  34. return next(error)
  35. }
  36. res.sendStatus(204)
  37. })
  38. })
  39. }
  40. async function resendConfirmation(req, res, next) {
  41. const userId = AuthenticationController.getLoggedInUserId(req)
  42. const email = EmailHelper.parseEmail(req.body.email)
  43. if (!email) {
  44. return res.sendStatus(422)
  45. }
  46. UserGetter.getUserByAnyEmail(email, { _id: 1 }, async function(error, user) {
  47. if (error) {
  48. return next(error)
  49. }
  50. if (!user || user._id.toString() !== userId) {
  51. logger.log(
  52. { userId, email, foundUserId: user && user._id },
  53. "email doesn't match logged in user"
  54. )
  55. return res.sendStatus(422)
  56. }
  57. if (Features.hasFeature('saml') || req.session.samlBeta) {
  58. // institution SSO emails cannot be confirmed by email,
  59. // confirmation happens by linking to the institution
  60. let institution
  61. try {
  62. institution = await InstitutionsAPI.getInstitutionViaDomain(
  63. email.split('@').pop()
  64. )
  65. } catch (error) {
  66. if (!(error instanceof Errors.NotFoundError)) {
  67. throw error
  68. }
  69. }
  70. if (institution && institution.sso_enabled) {
  71. return res.sendStatus(422)
  72. }
  73. }
  74. logger.log({ userId, email }, 'resending email confirmation token')
  75. UserEmailsConfirmationHandler.sendConfirmationEmail(userId, email, function(
  76. error
  77. ) {
  78. if (error) {
  79. return next(error)
  80. }
  81. res.sendStatus(200)
  82. })
  83. })
  84. }
  85. module.exports = UserEmailsController = {
  86. list(req, res, next) {
  87. const userId = AuthenticationController.getLoggedInUserId(req)
  88. UserGetter.getUserFullEmails(userId, function(error, fullEmails) {
  89. if (error) {
  90. return next(error)
  91. }
  92. res.json(fullEmails)
  93. })
  94. },
  95. add,
  96. remove(req, res, next) {
  97. const userId = AuthenticationController.getLoggedInUserId(req)
  98. const email = EmailHelper.parseEmail(req.body.email)
  99. if (!email) {
  100. return res.sendStatus(422)
  101. }
  102. UserUpdater.removeEmailAddress(userId, email, function(error) {
  103. if (error) {
  104. return next(error)
  105. }
  106. res.sendStatus(200)
  107. })
  108. },
  109. setDefault(req, res, next) {
  110. const userId = AuthenticationController.getLoggedInUserId(req)
  111. const email = EmailHelper.parseEmail(req.body.email)
  112. if (!email) {
  113. return res.sendStatus(422)
  114. }
  115. UserUpdater.setDefaultEmailAddress(userId, email, err => {
  116. if (err) {
  117. return UserEmailsController._handleEmailError(err, req, res, next)
  118. }
  119. AuthenticationController.setInSessionUser(req, { email: email })
  120. res.sendStatus(200)
  121. })
  122. },
  123. endorse(req, res, next) {
  124. const userId = AuthenticationController.getLoggedInUserId(req)
  125. const email = EmailHelper.parseEmail(req.body.email)
  126. if (!email) {
  127. return res.sendStatus(422)
  128. }
  129. endorseAffiliation(
  130. userId,
  131. email,
  132. req.body.role,
  133. req.body.department,
  134. function(error) {
  135. if (error) {
  136. return next(error)
  137. }
  138. res.sendStatus(204)
  139. }
  140. )
  141. },
  142. resendConfirmation,
  143. showConfirm(req, res, next) {
  144. res.render('user/confirm_email', {
  145. token: req.query.token,
  146. title: 'confirm_email'
  147. })
  148. },
  149. confirm(req, res, next) {
  150. const { token } = req.body
  151. if (!token) {
  152. return res.sendStatus(422)
  153. }
  154. UserEmailsConfirmationHandler.confirmEmailFromToken(token, function(error) {
  155. if (error) {
  156. if (error instanceof Errors.NotFoundError) {
  157. res.status(404).json({
  158. message:
  159. 'Sorry, your confirmation token is invalid or has expired. Please request a new email confirmation link.'
  160. })
  161. } else {
  162. next(error)
  163. }
  164. } else {
  165. res.sendStatus(200)
  166. }
  167. })
  168. },
  169. _handleEmailError(error, req, res, next) {
  170. if (error instanceof Errors.UnconfirmedEmailError) {
  171. return next(
  172. new HttpErrors.ConflictError({
  173. info: {
  174. public: { message: 'email must be confirmed' }
  175. }
  176. }).withCause(error)
  177. )
  178. } else if (error instanceof Errors.EmailExistsError) {
  179. return next(
  180. new HttpErrors.ConflictError({
  181. info: {
  182. public: { message: req.i18n.translate('email_already_registered') }
  183. }
  184. }).withCause(error)
  185. )
  186. }
  187. next(new HttpErrors.InternalServerError().withCause(error))
  188. }
  189. }