UserMembershipController.mjs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import SessionManager from '../Authentication/SessionManager.js'
  2. import UserMembershipHandler from './UserMembershipHandler.js'
  3. import Errors from '../Errors/Errors.js'
  4. import EmailHelper from '../Helpers/EmailHelper.js'
  5. import { csvAttachment } from '../../infrastructure/Response.js'
  6. import {
  7. UserIsManagerError,
  8. UserAlreadyAddedError,
  9. UserNotFoundError,
  10. } from './UserMembershipErrors.js'
  11. import { SSOConfig } from '../../models/SSOConfig.js'
  12. import { Parser as CSVParser } from 'json2csv'
  13. import { expressify } from '@overleaf/promise-utils'
  14. import PlansLocator from '../Subscription/PlansLocator.js'
  15. import RecurlyClient from '../Subscription/RecurlyClient.js'
  16. import Modules from '../../infrastructure/Modules.js'
  17. async function manageGroupMembers(req, res, next) {
  18. const { entity: subscription, entityConfig } = req
  19. const entityPrimaryKey =
  20. subscription[entityConfig.fields.primaryKey].toString()
  21. let entityName
  22. if (entityConfig.fields.name) {
  23. entityName = subscription[entityConfig.fields.name]
  24. }
  25. const users = await UserMembershipHandler.promises.getUsers(
  26. subscription,
  27. entityConfig
  28. )
  29. const ssoConfig = await SSOConfig.findById(subscription.ssoConfig).exec()
  30. const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
  31. const userId = SessionManager.getLoggedInUserId(req.session)?.toString()
  32. const isAdmin = subscription.admin_id.toString() === userId
  33. const isUserGroupManager =
  34. Boolean(subscription.manager_ids?.some(id => id.toString() === userId)) &&
  35. !isAdmin
  36. const recurlySubscription = subscription.recurlySubscription_id
  37. ? await RecurlyClient.promises.getSubscription(
  38. subscription.recurlySubscription_id
  39. )
  40. : undefined
  41. const canUseAddSeatsFeature =
  42. plan?.canUseFlexibleLicensing &&
  43. isAdmin &&
  44. recurlySubscription &&
  45. !recurlySubscription.pendingChange
  46. res.render('user_membership/group-members-react', {
  47. name: entityName,
  48. groupId: entityPrimaryKey,
  49. users,
  50. groupSize: subscription.membersLimit,
  51. managedUsersActive: subscription.managedUsersEnabled,
  52. isUserGroupManager,
  53. groupSSOActive: ssoConfig?.enabled,
  54. canUseFlexibleLicensing: plan?.canUseFlexibleLicensing,
  55. canUseAddSeatsFeature,
  56. })
  57. }
  58. async function manageGroupManagers(req, res, next) {
  59. await _renderManagersPage(
  60. req,
  61. res,
  62. next,
  63. 'user_membership/group-managers-react'
  64. )
  65. }
  66. async function manageInstitutionManagers(req, res, next) {
  67. await _renderManagersPage(
  68. req,
  69. res,
  70. next,
  71. 'user_membership/institution-managers-react'
  72. )
  73. }
  74. async function managePublisherManagers(req, res, next) {
  75. await _renderManagersPage(
  76. req,
  77. res,
  78. next,
  79. 'user_membership/publisher-managers-react'
  80. )
  81. }
  82. async function _renderManagersPage(req, res, next, template) {
  83. const { entity, entityConfig } = req
  84. const fetchV1Data = new Promise((resolve, reject) => {
  85. entity.fetchV1Data((error, entity) => {
  86. if (error) {
  87. reject(error)
  88. } else {
  89. resolve(entity)
  90. }
  91. })
  92. })
  93. const entityWithV1Data = await fetchV1Data
  94. const entityPrimaryKey =
  95. entityWithV1Data[entityConfig.fields.primaryKey].toString()
  96. let entityName
  97. if (entityConfig.fields.name) {
  98. entityName = entityWithV1Data[entityConfig.fields.name]
  99. }
  100. const users = await UserMembershipHandler.promises.getUsers(
  101. entityWithV1Data,
  102. entityConfig
  103. )
  104. res.render(template, {
  105. name: entityName,
  106. users,
  107. groupId: entityPrimaryKey,
  108. })
  109. }
  110. async function exportCsv(req, res) {
  111. let ssoEnabled
  112. const { entity, entityConfig } = req
  113. const fields = ['email', 'last_logged_in_at', 'last_active_at']
  114. const { managedUsersEnabled } = entity
  115. let users = await UserMembershipHandler.promises.getUsers(
  116. entity,
  117. entityConfig
  118. )
  119. if (entity.ssoConfig) {
  120. const ssoEnabledResult = await Modules.promises.hooks.fire(
  121. 'hasGroupSSOEnabled',
  122. entity
  123. )
  124. ssoEnabled = ssoEnabledResult?.[0]
  125. }
  126. if (managedUsersEnabled) {
  127. fields.push('managed')
  128. }
  129. if (ssoEnabled) {
  130. fields.push('sso')
  131. }
  132. if (managedUsersEnabled || ssoEnabled) {
  133. users = users.map(user => {
  134. if (managedUsersEnabled) {
  135. user.managed =
  136. user.enrollment?.managedBy?.toString() === entity._id.toString()
  137. }
  138. if (ssoEnabled) {
  139. user.sso = !!user.enrollment?.sso?.some(
  140. groupLinked =>
  141. groupLinked.groupId.toString() === entity._id.toString()
  142. )
  143. }
  144. return user
  145. })
  146. }
  147. const csvParser = new CSVParser({ fields })
  148. csvAttachment(res, csvParser.parse(users), 'Group.csv')
  149. }
  150. export default {
  151. manageGroupMembers: expressify(manageGroupMembers),
  152. manageGroupManagers: expressify(manageGroupManagers),
  153. manageInstitutionManagers: expressify(manageInstitutionManagers),
  154. managePublisherManagers: expressify(managePublisherManagers),
  155. add(req, res, next) {
  156. const { entity, entityConfig } = req
  157. const email = EmailHelper.parseEmail(req.body.email)
  158. if (email == null) {
  159. return res.status(400).json({
  160. error: {
  161. code: 'invalid_email',
  162. message: req.i18n.translate('invalid_email'),
  163. },
  164. })
  165. }
  166. if (entityConfig.readOnly) {
  167. return next(new Errors.NotFoundError('Cannot add users to entity'))
  168. }
  169. UserMembershipHandler.addUser(
  170. entity,
  171. entityConfig,
  172. email,
  173. function (error, user) {
  174. if (error && error instanceof UserAlreadyAddedError) {
  175. return res.status(400).json({
  176. error: {
  177. code: 'user_already_added',
  178. message: req.i18n.translate('user_already_added'),
  179. },
  180. })
  181. }
  182. if (error && error instanceof UserNotFoundError) {
  183. return res.status(404).json({
  184. error: {
  185. code: 'user_not_found',
  186. message: req.i18n.translate('user_not_found'),
  187. },
  188. })
  189. }
  190. if (error != null) {
  191. return next(error)
  192. }
  193. res.json({ user })
  194. }
  195. )
  196. },
  197. remove(req, res, next) {
  198. const { entity, entityConfig } = req
  199. const { userId } = req.params
  200. if (entityConfig.readOnly) {
  201. return next(new Errors.NotFoundError('Cannot remove users from entity'))
  202. }
  203. const loggedInUserId = SessionManager.getLoggedInUserId(req.session)
  204. if (loggedInUserId === userId) {
  205. return res.status(400).json({
  206. error: {
  207. code: 'managers_cannot_remove_self',
  208. message: req.i18n.translate('managers_cannot_remove_self'),
  209. },
  210. })
  211. }
  212. UserMembershipHandler.removeUser(
  213. entity,
  214. entityConfig,
  215. userId,
  216. function (error, user) {
  217. if (error && error instanceof UserIsManagerError) {
  218. return res.status(400).json({
  219. error: {
  220. code: 'managers_cannot_remove_admin',
  221. message: req.i18n.translate('managers_cannot_remove_admin'),
  222. },
  223. })
  224. }
  225. if (error != null) {
  226. return next(error)
  227. }
  228. res.sendStatus(200)
  229. }
  230. )
  231. },
  232. exportCsv: expressify(exportCsv),
  233. new(req, res, next) {
  234. res.render('user_membership/new', {
  235. entityName: req.params.name,
  236. entityId: req.params.id,
  237. })
  238. },
  239. create(req, res, next) {
  240. const entityId = req.params.id
  241. const entityConfig = req.entityConfig
  242. UserMembershipHandler.createEntity(
  243. entityId,
  244. entityConfig,
  245. function (error, entity) {
  246. if (error != null) {
  247. return next(error)
  248. }
  249. res.redirect(entityConfig.pathsFor(entityId).index)
  250. }
  251. )
  252. },
  253. }