remove_deleted_users_from_token_access_refs.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
  2. const { batchedUpdate } = require('./helpers/batchedUpdate')
  3. const { ObjectId } = require('mongodb')
  4. const minimist = require('minimist')
  5. const CollaboratorsHandler = require('../app/src/Features/Collaborators/CollaboratorsHandler')
  6. const argv = minimist(process.argv.slice(2), {
  7. string: ['projects'],
  8. boolean: ['dry-run', 'help'],
  9. alias: {
  10. projects: 'p',
  11. },
  12. default: {
  13. 'dry-run': true,
  14. },
  15. })
  16. if (argv.help || argv._.length > 1) {
  17. console.error(`Usage: node scripts/remove_deleted_users_from_token_access_refs.js [OPTS]
  18. Finds or removes deleted user ids from token access fields
  19. "tokenAccessReadOnly_refs" and "tokenAccessReadAndWrite_refs" in the "projects" collection.
  20. If no projects are specified, all projects will be processed.
  21. Options:
  22. --dry-run finds projects and deleted users but does not do any updates
  23. --projects list of projects ids to be fixed (comma separated)
  24. `)
  25. process.exit(1)
  26. }
  27. const DRY_RUN = argv['dry-run']
  28. const PROJECTS_LIST = argv.projects
  29. async function findUserIds() {
  30. const userIds = new Set()
  31. const cursor = db.users.find({}, { _id: 1 })
  32. for await (const user of cursor) {
  33. userIds.add(user._id.toString())
  34. }
  35. console.log(`=> User ids count: ${userIds.size}`)
  36. return userIds
  37. }
  38. async function fixProjectsWithInvalidTokenAccessRefsIds(
  39. DRY_RUN,
  40. PROJECTS_LIST
  41. ) {
  42. if (DRY_RUN) {
  43. console.log('=> Doing dry run')
  44. }
  45. const DELETED_USER_COLLABORATOR_IDS = new Set()
  46. const PROJECTS_WITH_DELETED_USER = new Set()
  47. // get a set of all users ids as an in-memory cache
  48. const userIds = await findUserIds()
  49. // default query for finding all projects with non-empty token access fields
  50. let query = {
  51. $or: [
  52. { 'tokenAccessReadOnly_refs.0': { $exists: true } },
  53. { 'tokenAccessReadAndWrite_refs.0': { $exists: true } },
  54. ],
  55. }
  56. const projectIds = PROJECTS_LIST?.split(',').map(
  57. projectId => new ObjectId(projectId)
  58. )
  59. // query for finding projects passed in as args
  60. if (projectIds) {
  61. query = { $and: [{ _id: { $in: projectIds } }] }
  62. }
  63. await batchedUpdate(
  64. 'projects',
  65. query,
  66. async projects => {
  67. for (const project of projects) {
  68. // find the set of user ids that are in the token access fields
  69. // i.e. the set of collaborators
  70. const collaboratorIds = new Set()
  71. for (const roUserId of project.tokenAccessReadOnly_refs) {
  72. collaboratorIds.add(roUserId.toString())
  73. }
  74. for (const rwUserId of project.tokenAccessReadAndWrite_refs) {
  75. collaboratorIds.add(rwUserId.toString())
  76. }
  77. // determine which collaborator ids are not in the `users` collection
  78. // i.e. the user has been deleted
  79. const deletedUserIds = new Set()
  80. for (const collaboratorId of collaboratorIds) {
  81. if (!userIds.has(collaboratorId)) {
  82. deletedUserIds.add(collaboratorId)
  83. }
  84. }
  85. // double-check that users doesn't exist in the users collection
  86. // we don't want to remove users that were added after the initial query
  87. const existingUsersCursor = db.users.find(
  88. { _id: { $in: [...deletedUserIds].map(id => new ObjectId(id)) } },
  89. { _id: 1 }
  90. )
  91. for await (const user of existingUsersCursor) {
  92. const id = user._id.toString()
  93. deletedUserIds.delete(id)
  94. // add the user id to the cache
  95. userIds.add(id)
  96. }
  97. // remove the actual deleted users
  98. for (const deletedUserId of deletedUserIds) {
  99. DELETED_USER_COLLABORATOR_IDS.add(deletedUserId)
  100. PROJECTS_WITH_DELETED_USER.add(project._id.toString())
  101. console.log(
  102. '=> Found deleted user id:',
  103. deletedUserId,
  104. 'in project:',
  105. project._id.toString()
  106. )
  107. if (DRY_RUN) {
  108. console.log(
  109. `=> DRY RUN - would remove deleted ${deletedUserId} from all projects (found in project ${project._id.toString()})`
  110. )
  111. continue
  112. }
  113. console.log(
  114. `=> Removing deleted ${deletedUserId} from all projects (found in project ${project._id.toString()})`
  115. )
  116. await CollaboratorsHandler.promises.removeUserFromAllProjects(
  117. new ObjectId(deletedUserId)
  118. )
  119. }
  120. }
  121. },
  122. { tokenAccessReadOnly_refs: 1, tokenAccessReadAndWrite_refs: 1 }
  123. )
  124. console.log(
  125. `=> ${DRY_RUN ? 'DRY RUN - would delete' : 'Deleted'} user ids (${
  126. DELETED_USER_COLLABORATOR_IDS.size
  127. })`
  128. )
  129. if (DELETED_USER_COLLABORATOR_IDS.size) {
  130. console.log(Array.from(DELETED_USER_COLLABORATOR_IDS).join('\n'))
  131. }
  132. console.log(
  133. `=> Projects with deleted user ids (${PROJECTS_WITH_DELETED_USER.size})`
  134. )
  135. if (PROJECTS_WITH_DELETED_USER.size) {
  136. console.log(Array.from(PROJECTS_WITH_DELETED_USER).join('\n'))
  137. }
  138. }
  139. async function main(DRY_RUN, PROJECTS_LIST) {
  140. await waitForDb()
  141. await fixProjectsWithInvalidTokenAccessRefsIds(DRY_RUN, PROJECTS_LIST)
  142. }
  143. module.exports = main
  144. if (require.main === module) {
  145. main(DRY_RUN, PROJECTS_LIST)
  146. .then(() => {
  147. console.error('Done')
  148. process.exit(0)
  149. })
  150. .catch(err => {
  151. console.error(err)
  152. process.exit(1)
  153. })
  154. }