UserMembershipHandler.mjs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import mongodb from 'mongodb-legacy'
  2. import { callbackify } from '@overleaf/promise-utils'
  3. import { Institution } from '../../models/Institution.mjs'
  4. import { Subscription } from '../../models/Subscription.mjs'
  5. import { Publisher } from '../../models/Publisher.mjs'
  6. import UserMembershipViewModel from './UserMembershipViewModel.mjs'
  7. import UserGetter from '../User/UserGetter.mjs'
  8. import UserMembershipErrors from './UserMembershipErrors.mjs'
  9. import Modules from '../../infrastructure/Modules.mjs'
  10. import mongoose from '../../infrastructure/Mongoose.mjs'
  11. const { ObjectId } = mongodb
  12. const EntityModels = { Institution, Subscription, Publisher }
  13. const UserMembershipHandler = {
  14. async getEntityWithoutAuthorizationCheck(entityId, entityConfig) {
  15. const query = buildEntityQuery(entityId, entityConfig)
  16. return await EntityModels[entityConfig.modelName].findOne(query).exec()
  17. },
  18. async createEntity(entityId, entityConfig) {
  19. const data = buildEntityQuery(entityId, entityConfig)
  20. return await EntityModels[entityConfig.modelName].create(data)
  21. },
  22. async getUsers(entity, entityConfig) {
  23. const attributes = entityConfig.fields.read
  24. return await getPopulatedListOfMembers(entity, attributes)
  25. },
  26. async addUser(entity, entityConfig, email, auditInfo) {
  27. const attribute = entityConfig.fields.write
  28. const user = await UserGetter.promises.getUserByAnyEmail(email)
  29. if (!user) {
  30. throw new UserMembershipErrors.UserNotFoundError()
  31. }
  32. if (entity[attribute].some(managerId => managerId.equals(user._id))) {
  33. throw new UserMembershipErrors.UserAlreadyAddedError()
  34. }
  35. // if the entity is a Subscription with managed users enabled, then audit log the event
  36. if (
  37. entityConfig.modelName === 'Subscription' &&
  38. entity.managedUsersEnabled
  39. ) {
  40. const session = await mongoose.startSession()
  41. try {
  42. await session.withTransaction(async () => {
  43. const auditLog = {
  44. groupId: entity._id,
  45. operation: 'group-role-changed',
  46. initiatorId: auditInfo.initiatorId,
  47. ipAddress: auditInfo.ipAddress,
  48. info: {
  49. userId: user._id,
  50. role: 'manager',
  51. },
  52. }
  53. await Modules.promises.hooks.fire(
  54. 'addGroupAuditLogEntry',
  55. auditLog,
  56. session
  57. )
  58. })
  59. } finally {
  60. await session.endSession()
  61. }
  62. }
  63. await addUserToEntity(entity, attribute, user)
  64. return UserMembershipViewModel.build(user)
  65. },
  66. async removeUser(entity, entityConfig, userId, auditInfo) {
  67. const attribute = entityConfig.fields.write
  68. if (entity.admin_id ? entity.admin_id.equals(userId) : undefined) {
  69. throw new UserMembershipErrors.UserIsManagerError()
  70. }
  71. // if the entity is a Subscription with managed users enabled, then audit log the event
  72. if (
  73. entityConfig.modelName === 'Subscription' &&
  74. entity.managedUsersEnabled
  75. ) {
  76. const auditLog = {
  77. groupId: entity._id,
  78. operation: 'group-role-changed',
  79. initiatorId: auditInfo.initiatorId,
  80. ipAddress: auditInfo.ipAddress,
  81. info: {
  82. userId,
  83. role: 'member',
  84. },
  85. }
  86. await Modules.promises.hooks.fire('addGroupAuditLogEntry', auditLog)
  87. }
  88. return await removeUserFromEntity(entity, attribute, userId)
  89. },
  90. }
  91. async function getPopulatedListOfMembers(entity, attributes) {
  92. const userObjects = []
  93. for (const attribute of attributes) {
  94. for (const userObject of entity[attribute] || []) {
  95. // userObject can be an email as String, a user id as ObjectId or an
  96. // invite as Object with an email attribute as String. We want to pass to
  97. // UserMembershipViewModel either an email as (String) or a user id (ObjectId)
  98. const userIdOrEmail = userObject.email || userObject
  99. userObjects.push(userIdOrEmail)
  100. }
  101. }
  102. const users = await UserMembershipViewModel.promises.buildAsync(userObjects)
  103. for (const user of users) {
  104. if (
  105. user?._id &&
  106. entity?.admin_id &&
  107. user._id.toString() === entity.admin_id.toString()
  108. ) {
  109. user.isEntityAdmin = true
  110. }
  111. if (
  112. user?._id &&
  113. entity?.manager_ids &&
  114. entity.manager_ids.includes(user._id)
  115. ) {
  116. user.isEntityManager = true
  117. }
  118. if (
  119. user?._id &&
  120. entity?.member_ids &&
  121. entity.member_ids.includes(user._id)
  122. ) {
  123. user.isEntityMember = true
  124. }
  125. }
  126. return users
  127. }
  128. async function addUserToEntity(entity, attribute, user) {
  129. const fieldUpdate = {}
  130. fieldUpdate[attribute] = user._id
  131. return await entity.updateOne({ $addToSet: fieldUpdate }).exec()
  132. }
  133. async function removeUserFromEntity(entity, attribute, userId) {
  134. const fieldUpdate = {}
  135. fieldUpdate[attribute] = userId
  136. return await entity.updateOne({ $pull: fieldUpdate }).exec()
  137. }
  138. function buildEntityQuery(entityId, entityConfig) {
  139. if (ObjectId.isValid(entityId.toString())) {
  140. entityId = new ObjectId(entityId)
  141. }
  142. const query = Object.assign({}, entityConfig.baseQuery)
  143. query[entityConfig.fields.primaryKey] = entityId
  144. return query
  145. }
  146. export default {
  147. getEntityWithoutAuthorizationCheck: callbackify(
  148. UserMembershipHandler.getEntityWithoutAuthorizationCheck
  149. ),
  150. createEntity: callbackify(UserMembershipHandler.createEntity),
  151. getUsers: callbackify(UserMembershipHandler.getUsers),
  152. addUser: callbackify(UserMembershipHandler.addUser),
  153. removeUser: callbackify(UserMembershipHandler.removeUser),
  154. promises: UserMembershipHandler,
  155. }