UserGetter.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. const mongojs = require('../../infrastructure/mongojs')
  2. const metrics = require('metrics-sharelatex')
  3. const logger = require('logger-sharelatex')
  4. const { db } = mongojs
  5. const { ObjectId } = mongojs
  6. const { promisifyAll } = require('../../util/promises')
  7. const { getUserAffiliations } = require('../Institutions/InstitutionsAPI')
  8. const Errors = require('../Errors/Errors')
  9. const Features = require('../../infrastructure/Features')
  10. const UserGetter = {
  11. getUser(query, projection, callback) {
  12. if (!query) {
  13. return callback(new Error('no query provided'))
  14. }
  15. if (arguments.length === 2) {
  16. callback = projection
  17. projection = {}
  18. }
  19. if (typeof query === 'string') {
  20. try {
  21. query = { _id: ObjectId(query) }
  22. } catch (e) {
  23. return callback(null, null)
  24. }
  25. } else if (query instanceof ObjectId) {
  26. query = { _id: query }
  27. }
  28. db.users.findOne(query, projection, callback)
  29. },
  30. getUserEmail(userId, callback) {
  31. this.getUser(userId, { email: 1 }, (error, user) =>
  32. callback(error, user && user.email)
  33. )
  34. },
  35. getUserFullEmails(userId, callback) {
  36. this.getUser(userId, { email: 1, emails: 1 }, function(error, user) {
  37. if (error) {
  38. return callback(error)
  39. }
  40. if (!user) {
  41. return callback(new Error('User not Found'))
  42. }
  43. if (!Features.hasFeature('affiliations')) {
  44. return callback(null, decorateFullEmails(user.email, user.emails, []))
  45. }
  46. getUserAffiliations(userId, function(error, affiliationsData) {
  47. if (error) {
  48. return callback(error)
  49. }
  50. callback(
  51. null,
  52. decorateFullEmails(user.email, user.emails || [], affiliationsData)
  53. )
  54. })
  55. })
  56. },
  57. getUserByMainEmail(email, projection, callback) {
  58. email = email.trim()
  59. if (arguments.length === 2) {
  60. callback = projection
  61. projection = {}
  62. }
  63. db.users.findOne({ email }, projection, callback)
  64. },
  65. getUserByAnyEmail(email, projection, callback) {
  66. email = email.trim()
  67. if (arguments.length === 2) {
  68. callback = projection
  69. projection = {}
  70. }
  71. // $exists: true MUST be set to use the partial index
  72. const query = { emails: { $exists: true }, 'emails.email': email }
  73. db.users.findOne(query, projection, (error, user) => {
  74. if (error || user) {
  75. return callback(error, user)
  76. }
  77. // While multiple emails are being rolled out, check for the main email as
  78. // well
  79. this.getUserByMainEmail(email, projection, callback)
  80. })
  81. },
  82. getUsersByAnyConfirmedEmail(emails, projection, callback) {
  83. if (arguments.length === 2) {
  84. callback = projection
  85. projection = {}
  86. }
  87. // $exists: true MUST be set to use the partial index
  88. const query = {
  89. emails: {
  90. $exists: true,
  91. $elemMatch: { email: { $in: emails }, confirmedAt: { $exists: true } }
  92. }
  93. }
  94. db.users.find(query, projection, callback)
  95. },
  96. getUsersByV1Ids(v1Ids, projection, callback) {
  97. if (arguments.length === 2) {
  98. callback = projection
  99. projection = {}
  100. }
  101. const query = { 'overleaf.id': { $in: v1Ids } }
  102. db.users.find(query, projection, callback)
  103. },
  104. getUsersByHostname(hostname, projection, callback) {
  105. const reversedHostname = hostname
  106. .trim()
  107. .split('')
  108. .reverse()
  109. .join('')
  110. const query = {
  111. emails: { $exists: true },
  112. 'emails.reversedHostname': reversedHostname
  113. }
  114. db.users.find(query, projection, callback)
  115. },
  116. getUsers(userIds, projection, callback) {
  117. try {
  118. userIds = userIds.map(u => ObjectId(u.toString()))
  119. } catch (error) {
  120. return callback(error)
  121. }
  122. db.users.find({ _id: { $in: userIds } }, projection, callback)
  123. },
  124. // check for duplicate email address. This is also enforced at the DB level
  125. ensureUniqueEmailAddress(newEmail, callback) {
  126. this.getUserByAnyEmail(newEmail, function(error, user) {
  127. if (user) {
  128. return callback(new Errors.EmailExistsError())
  129. }
  130. callback(error)
  131. })
  132. }
  133. }
  134. var decorateFullEmails = (defaultEmail, emailsData, affiliationsData) =>
  135. emailsData.map(function(emailData) {
  136. emailData.default = emailData.email === defaultEmail
  137. const affiliation = affiliationsData.find(
  138. aff => aff.email === emailData.email
  139. )
  140. if (affiliation) {
  141. const { institution, inferred, role, department } = affiliation
  142. emailData.affiliation = { institution, inferred, role, department }
  143. } else {
  144. emailsData.affiliation = null
  145. }
  146. return emailData
  147. })
  148. ;[
  149. 'getUser',
  150. 'getUserEmail',
  151. 'getUserByMainEmail',
  152. 'getUserByAnyEmail',
  153. 'getUsers',
  154. 'ensureUniqueEmailAddress'
  155. ].map(method =>
  156. metrics.timeAsyncMethod(UserGetter, method, 'mongo.UserGetter', logger)
  157. )
  158. UserGetter.promises = promisifyAll(UserGetter)
  159. module.exports = UserGetter