UserGetter.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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. // $exists: true MUST be set to use the partial index
  92. const query = {
  93. emails: {
  94. $exists: true,
  95. $elemMatch: { email: { $in: emails }, confirmedAt: { $exists: true } }
  96. }
  97. }
  98. db.users.find(query, { projection }).toArray(callback)
  99. },
  100. getUsersByV1Ids(v1Ids, projection, callback) {
  101. if (arguments.length === 2) {
  102. callback = projection
  103. projection = {}
  104. }
  105. const query = { 'overleaf.id': { $in: v1Ids } }
  106. db.users.find(query, { projection }).toArray(callback)
  107. },
  108. getUsersByHostname(hostname, projection, callback) {
  109. const reversedHostname = hostname
  110. .trim()
  111. .split('')
  112. .reverse()
  113. .join('')
  114. const query = {
  115. emails: { $exists: true },
  116. 'emails.reversedHostname': reversedHostname
  117. }
  118. db.users.find(query, { projection }).toArray(callback)
  119. },
  120. getUsers(query, projection, callback) {
  121. try {
  122. query = normalizeMultiQuery(query)
  123. db.users.find(query, { projection }).toArray(callback)
  124. } catch (err) {
  125. callback(err)
  126. }
  127. },
  128. // check for duplicate email address. This is also enforced at the DB level
  129. ensureUniqueEmailAddress(newEmail, callback) {
  130. this.getUserByAnyEmail(newEmail, function(error, user) {
  131. if (user) {
  132. return callback(new Errors.EmailExistsError())
  133. }
  134. callback(error)
  135. })
  136. }
  137. }
  138. var decorateFullEmails = (
  139. defaultEmail,
  140. emailsData,
  141. affiliationsData,
  142. samlIdentifiers
  143. ) =>
  144. emailsData.map(function(emailData) {
  145. emailData.default = emailData.email === defaultEmail
  146. const affiliation = affiliationsData.find(
  147. aff => aff.email === emailData.email
  148. )
  149. if (affiliation) {
  150. const { institution, inferred, role, department, licence } = affiliation
  151. emailData.affiliation = {
  152. institution,
  153. inferred,
  154. role,
  155. department,
  156. licence
  157. }
  158. } else {
  159. emailsData.affiliation = null
  160. }
  161. if (emailData.samlProviderId) {
  162. emailData.samlIdentifier = samlIdentifiers.find(
  163. samlIdentifier => samlIdentifier.providerId === emailData.samlProviderId
  164. )
  165. } else {
  166. emailsData.samlIdentifier = null
  167. }
  168. emailData.emailHasInstitutionLicence = InstitutionsHelper.emailHasLicence(
  169. emailData
  170. )
  171. return emailData
  172. })
  173. ;[
  174. 'getUser',
  175. 'getUserEmail',
  176. 'getUserByMainEmail',
  177. 'getUserByAnyEmail',
  178. 'getUsers',
  179. 'ensureUniqueEmailAddress'
  180. ].map(method =>
  181. metrics.timeAsyncMethod(UserGetter, method, 'mongo.UserGetter', logger)
  182. )
  183. UserGetter.promises = promisifyAll(UserGetter)
  184. module.exports = UserGetter