UserMembershipHandler.mjs 5.9 KB

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