UserUpdater.coffee 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. logger = require("logger-sharelatex")
  2. mongojs = require("../../infrastructure/mongojs")
  3. metrics = require("metrics-sharelatex")
  4. db = mongojs.db
  5. async = require("async")
  6. ObjectId = mongojs.ObjectId
  7. UserGetter = require("./UserGetter")
  8. { addAffiliation, removeAffiliation } = require("../Institutions/InstitutionsAPI")
  9. FeaturesUpdater = require("../Subscription/FeaturesUpdater")
  10. EmailHelper = require "../Helpers/EmailHelper"
  11. Errors = require "../Errors/Errors"
  12. Settings = require "settings-sharelatex"
  13. request = require 'request'
  14. NewsletterManager = require "../Newsletter/NewsletterManager"
  15. module.exports = UserUpdater =
  16. updateUser: (query, update, callback = (error) ->) ->
  17. if typeof query == "string"
  18. query = _id: ObjectId(query)
  19. else if query instanceof ObjectId
  20. query = _id: query
  21. else if typeof query._id == "string"
  22. query._id = ObjectId(query._id)
  23. db.users.update query, update, callback
  24. #
  25. # DEPRECATED
  26. #
  27. # Change the user's main email address by adding a new email, switching the
  28. # default email and removing the old email. Prefer manipulating multiple
  29. # emails and the default rather than calling this method directly
  30. #
  31. changeEmailAddress: (userId, newEmail, callback)->
  32. newEmail = EmailHelper.parseEmail(newEmail)
  33. return callback(new Error('invalid email')) if !newEmail?
  34. logger.log userId: userId, newEmail: newEmail, "updaing email address of user"
  35. oldEmail = null
  36. async.series [
  37. (cb) ->
  38. UserGetter.getUserEmail userId, (error, email) ->
  39. oldEmail = email
  40. cb(error)
  41. (cb) -> UserUpdater.addEmailAddress userId, newEmail, cb
  42. (cb) -> UserUpdater.setDefaultEmailAddress userId, newEmail, cb
  43. (cb) -> UserUpdater.removeEmailAddress userId, oldEmail, cb
  44. ], callback
  45. # Add a new email address for the user. Email cannot be already used by this
  46. # or any other user
  47. addEmailAddress: (userId, newEmail, affiliationOptions, callback) ->
  48. unless callback? # affiliationOptions is optional
  49. callback = affiliationOptions
  50. affiliationOptions = {}
  51. newEmail = EmailHelper.parseEmail(newEmail)
  52. return callback(new Error('invalid email')) if !newEmail?
  53. UserGetter.ensureUniqueEmailAddress newEmail, (error) =>
  54. return callback(error) if error?
  55. addAffiliation userId, newEmail, affiliationOptions, (error) =>
  56. if error?
  57. logger.err error: error, 'problem adding affiliation while adding email'
  58. return callback(error)
  59. reversedHostname = newEmail.split('@')[1].split('').reverse().join('')
  60. update = $push: emails: email: newEmail, createdAt: new Date(), reversedHostname: reversedHostname
  61. @updateUser userId, update, (error) ->
  62. if error?
  63. logger.err error: error, 'problem updating users emails'
  64. return callback(error)
  65. callback()
  66. # remove one of the user's email addresses. The email cannot be the user's
  67. # default email address
  68. removeEmailAddress: (userId, email, callback) ->
  69. email = EmailHelper.parseEmail(email)
  70. return callback(new Error('invalid email')) if !email?
  71. removeAffiliation userId, email, (error) =>
  72. if error?
  73. logger.err error: error, 'problem removing affiliation'
  74. return callback(error)
  75. query = _id: userId, email: $ne: email
  76. update = $pull: emails: email: email
  77. @updateUser query, update, (error, res) ->
  78. if error?
  79. logger.err error:error, 'problem removing users email'
  80. return callback(error)
  81. if res.n == 0
  82. return callback(new Error('Cannot remove email'))
  83. callback()
  84. # set the default email address by setting the `email` attribute. The email
  85. # must be one of the user's multiple emails (`emails` attribute)
  86. setDefaultEmailAddress: (userId, email, callback) ->
  87. email = EmailHelper.parseEmail(email)
  88. return callback(new Error('invalid email')) if !email?
  89. UserGetter.getUserEmail userId, (error, oldEmail) =>
  90. if err?
  91. return callback(error)
  92. query = _id: userId, 'emails.email': email
  93. update = $set: email: email
  94. @updateUser query, update, (error, res) ->
  95. if error?
  96. logger.err error:error, 'problem setting default emails'
  97. return callback(error)
  98. else if res.n == 0 # TODO: Check n or nMatched?
  99. return callback(new Error('Default email does not belong to user'))
  100. else
  101. NewsletterManager.changeEmail oldEmail, email, ->
  102. callback()
  103. updateV1AndSetDefaultEmailAddress: (userId, email, callback) ->
  104. @updateEmailAddressInV1 userId, email, (error) =>
  105. return callback(error) if error?
  106. @setDefaultEmailAddress userId, email, callback
  107. updateEmailAddressInV1: (userId, newEmail, callback) ->
  108. if !Settings.apis?.v1?.url?
  109. return callback()
  110. UserGetter.getUser userId, { 'overleaf.id': 1, emails: 1 }, (error, user) ->
  111. return callback(error) if error?
  112. return callback(new Errors.NotFoundError('no user found')) if !user?
  113. if !user.overleaf?.id?
  114. return callback()
  115. newEmailIsConfirmed = false
  116. for email in user.emails
  117. if email.email == newEmail and email.confirmedAt?
  118. newEmailIsConfirmed = true
  119. break
  120. if !newEmailIsConfirmed
  121. return callback(new Errors.UnconfirmedEmailError("can't update v1 with unconfirmed email"))
  122. request {
  123. baseUrl: Settings.apis.v1.url
  124. url: "/api/v1/sharelatex/users/#{user.overleaf.id}/email"
  125. method: 'PUT'
  126. auth:
  127. user: Settings.apis.v1.user
  128. pass: Settings.apis.v1.pass
  129. sendImmediately: true
  130. json:
  131. user:
  132. email: newEmail
  133. timeout: 5 * 1000
  134. }, (error, response, body) ->
  135. if error?
  136. error = new Errors.V1ConnectionError('No V1 connection') if error.code == 'ECONNREFUSED'
  137. return callback(error)
  138. if response.statusCode == 409 # Conflict
  139. return callback(new Errors.EmailExistsError('email exists in v1'))
  140. else if 200 <= response.statusCode < 300
  141. return callback()
  142. else
  143. return callback new Error("non-success code from v1: #{response.statusCode}")
  144. confirmEmail: (userId, email, confirmedAt, callback) ->
  145. if arguments.length == 3
  146. callback = confirmedAt
  147. confirmedAt = new Date()
  148. email = EmailHelper.parseEmail(email)
  149. return callback(new Error('invalid email')) if !email?
  150. logger.log {userId, email}, 'confirming user email'
  151. addAffiliation userId, email, {confirmedAt: confirmedAt}, (error) =>
  152. if error?
  153. logger.err error: error, 'problem adding affiliation while confirming email'
  154. return callback(error)
  155. query =
  156. _id: userId
  157. 'emails.email': email
  158. update =
  159. $set:
  160. 'emails.$.confirmedAt': confirmedAt
  161. @updateUser query, update, (error, res) ->
  162. return callback(error) if error?
  163. logger.log {res, userId, email}, "tried to confirm email"
  164. if res.n == 0
  165. return callback(new Errors.NotFoundError('user id and email do no match'))
  166. FeaturesUpdater.refreshFeatures userId, true, callback
  167. [
  168. 'updateUser'
  169. 'changeEmailAddress'
  170. 'setDefaultEmailAddress'
  171. 'addEmailAddress'
  172. 'removeEmailAddress'
  173. ].map (method) ->
  174. metrics.timeAsyncMethod(UserUpdater, method, 'mongo.UserUpdater', logger)