UserGetter.js 5.3 KB

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