UserMembershipController.mjs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 SplitTestHandler from '../SplitTests/SplitTestHandler.js'
  15. import PlansLocator from '../Subscription/PlansLocator.js'
  16. import RecurlyClient from '../Subscription/RecurlyClient.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. await SplitTestHandler.promises.getAssignment(
  31. req,
  32. res,
  33. 'flexible-group-licensing'
  34. )
  35. await SplitTestHandler.promises.getAssignment(req, res, 'bootstrap-5-groups')
  36. const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
  37. const userId = SessionManager.getLoggedInUserId(req.session)
  38. const isAdmin = subscription.admin_id.toString() === userId
  39. const recurlySubscription = subscription.recurlySubscription_id
  40. ? await RecurlyClient.promises.getSubscription(
  41. subscription.recurlySubscription_id
  42. )
  43. : undefined
  44. const canUseAddSeatsFeature =
  45. plan?.canUseFlexibleLicensing &&
  46. isAdmin &&
  47. recurlySubscription &&
  48. !recurlySubscription.pendingChange
  49. res.render('user_membership/group-members-react', {
  50. name: entityName,
  51. groupId: entityPrimaryKey,
  52. users,
  53. groupSize: subscription.membersLimit,
  54. managedUsersActive: subscription.managedUsersEnabled,
  55. groupSSOActive: ssoConfig?.enabled,
  56. canUseFlexibleLicensing: plan?.canUseFlexibleLicensing,
  57. canUseAddSeatsFeature,
  58. })
  59. }
  60. async function manageGroupManagers(req, res, next) {
  61. await _renderManagersPage(
  62. req,
  63. res,
  64. next,
  65. 'user_membership/group-managers-react'
  66. )
  67. }
  68. async function manageInstitutionManagers(req, res, next) {
  69. await _renderManagersPage(
  70. req,
  71. res,
  72. next,
  73. 'user_membership/institution-managers-react'
  74. )
  75. }
  76. async function managePublisherManagers(req, res, next) {
  77. await _renderManagersPage(
  78. req,
  79. res,
  80. next,
  81. 'user_membership/publisher-managers-react'
  82. )
  83. }
  84. async function _renderManagersPage(req, res, next, template) {
  85. const { entity, entityConfig } = req
  86. const fetchV1Data = new Promise((resolve, reject) => {
  87. entity.fetchV1Data((error, entity) => {
  88. if (error) {
  89. reject(error)
  90. } else {
  91. resolve(entity)
  92. }
  93. })
  94. })
  95. const entityWithV1Data = await fetchV1Data
  96. const entityPrimaryKey =
  97. entityWithV1Data[entityConfig.fields.primaryKey].toString()
  98. let entityName
  99. if (entityConfig.fields.name) {
  100. entityName = entityWithV1Data[entityConfig.fields.name]
  101. }
  102. const users = await UserMembershipHandler.promises.getUsers(
  103. entityWithV1Data,
  104. entityConfig
  105. )
  106. await SplitTestHandler.promises.getAssignment(req, res, 'bootstrap-5-groups')
  107. res.render(template, {
  108. name: entityName,
  109. users,
  110. groupId: entityPrimaryKey,
  111. })
  112. }
  113. export default {
  114. manageGroupMembers: expressify(manageGroupMembers),
  115. manageGroupManagers: expressify(manageGroupManagers),
  116. manageInstitutionManagers: expressify(manageInstitutionManagers),
  117. managePublisherManagers: expressify(managePublisherManagers),
  118. add(req, res, next) {
  119. const { entity, entityConfig } = req
  120. const email = EmailHelper.parseEmail(req.body.email)
  121. if (email == null) {
  122. return res.status(400).json({
  123. error: {
  124. code: 'invalid_email',
  125. message: req.i18n.translate('invalid_email'),
  126. },
  127. })
  128. }
  129. if (entityConfig.readOnly) {
  130. return next(new Errors.NotFoundError('Cannot add users to entity'))
  131. }
  132. UserMembershipHandler.addUser(
  133. entity,
  134. entityConfig,
  135. email,
  136. function (error, user) {
  137. if (error && error instanceof UserAlreadyAddedError) {
  138. return res.status(400).json({
  139. error: {
  140. code: 'user_already_added',
  141. message: req.i18n.translate('user_already_added'),
  142. },
  143. })
  144. }
  145. if (error && error instanceof UserNotFoundError) {
  146. return res.status(404).json({
  147. error: {
  148. code: 'user_not_found',
  149. message: req.i18n.translate('user_not_found'),
  150. },
  151. })
  152. }
  153. if (error != null) {
  154. return next(error)
  155. }
  156. res.json({ user })
  157. }
  158. )
  159. },
  160. remove(req, res, next) {
  161. const { entity, entityConfig } = req
  162. const { userId } = req.params
  163. if (entityConfig.readOnly) {
  164. return next(new Errors.NotFoundError('Cannot remove users from entity'))
  165. }
  166. const loggedInUserId = SessionManager.getLoggedInUserId(req.session)
  167. if (loggedInUserId === userId) {
  168. return res.status(400).json({
  169. error: {
  170. code: 'managers_cannot_remove_self',
  171. message: req.i18n.translate('managers_cannot_remove_self'),
  172. },
  173. })
  174. }
  175. UserMembershipHandler.removeUser(
  176. entity,
  177. entityConfig,
  178. userId,
  179. function (error, user) {
  180. if (error && error instanceof UserIsManagerError) {
  181. return res.status(400).json({
  182. error: {
  183. code: 'managers_cannot_remove_admin',
  184. message: req.i18n.translate('managers_cannot_remove_admin'),
  185. },
  186. })
  187. }
  188. if (error != null) {
  189. return next(error)
  190. }
  191. res.sendStatus(200)
  192. }
  193. )
  194. },
  195. exportCsv(req, res, next) {
  196. const { entity, entityConfig } = req
  197. const fields = ['email', 'last_logged_in_at', 'last_active_at']
  198. UserMembershipHandler.getUsers(
  199. entity,
  200. entityConfig,
  201. function (error, users) {
  202. if (error != null) {
  203. return next(error)
  204. }
  205. const csvParser = new CSVParser({ fields })
  206. csvAttachment(res, csvParser.parse(users), 'Group.csv')
  207. }
  208. )
  209. },
  210. new(req, res, next) {
  211. res.render('user_membership/new', {
  212. entityName: req.params.name,
  213. entityId: req.params.id,
  214. })
  215. },
  216. create(req, res, next) {
  217. const entityId = req.params.id
  218. const entityConfig = req.entityConfig
  219. UserMembershipHandler.createEntity(
  220. entityId,
  221. entityConfig,
  222. function (error, entity) {
  223. if (error != null) {
  224. return next(error)
  225. }
  226. res.redirect(entityConfig.pathsFor(entityId).index)
  227. }
  228. )
  229. },
  230. }