update_group_policy.mjs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import fs from 'node:fs'
  2. import minimist from 'minimist'
  3. import { ObjectId } from '../app/src/infrastructure/mongodb.mjs'
  4. import GroupPoliciesHandler from '../modules/group-settings/app/src/GroupPoliciesHandler.mjs'
  5. import { scriptRunner } from './lib/ScriptRunner.mjs'
  6. // Imported to ensure policies are registered before we attempt to update them
  7. import '../modules/group-settings/app/src/managed-users/ManagedUsersPolicy.mjs'
  8. import '../modules/institutions/app/src/InstitutionsPolicy.mjs'
  9. const USAGE = `Usage:
  10. node scripts/update_group_policy.mjs --subscription-id=<id> --policy-name=<name> --policy-value=<true|false>
  11. node scripts/update_group_policy.mjs --file=<path> --policy-name=<name> --policy-value=<true|false>
  12. Options:
  13. --subscription-id The id of a single subscription to update
  14. --file Path to a file with one subscription _id per line (ignored when --subscription-id is present)
  15. --policy-name The policy name to update (e.g. userCannotUseAIFeatures)
  16. --policy-value The policy value (true or false)
  17. --dry-run Perform the search but only log the count of subscriptions to update. Defaults to true
  18. --initiator-id The user id of the person running the script (for audit logs)
  19. --verbose Log each subscription id being updated
  20. --help Show this help message`
  21. function printUsageAndExit(code) {
  22. console.log(USAGE)
  23. process.exit(code)
  24. }
  25. const argv = minimist(process.argv.slice(2), {
  26. string: [
  27. 'subscription-id',
  28. 'policy-name',
  29. 'policy-value',
  30. 'initiator-id',
  31. 'file',
  32. ],
  33. boolean: ['dry-run', 'help', 'verbose'],
  34. default: { 'dry-run': true, verbose: false },
  35. })
  36. if (argv.help) {
  37. printUsageAndExit(0)
  38. }
  39. async function main() {
  40. const subscriptionId = argv['subscription-id']
  41. const policyName = argv['policy-name']
  42. const policyValue = argv['policy-value']
  43. const filePath = argv.file
  44. const dryRun = argv['dry-run']
  45. const initiatorId = argv['initiator-id']
  46. const verbose = argv.verbose
  47. if (!policyName) {
  48. console.error('Missing --policy-name argument')
  49. printUsageAndExit(1)
  50. }
  51. if (!subscriptionId && !filePath) {
  52. console.error(
  53. 'Missing --subscription-id or --file argument, one must be provided'
  54. )
  55. printUsageAndExit(1)
  56. }
  57. if (!policyValue) {
  58. console.error('Missing --policy-value argument')
  59. printUsageAndExit(1)
  60. }
  61. if (policyValue !== 'true' && policyValue !== 'false') {
  62. console.error('--policy-value must be "true" or "false"')
  63. printUsageAndExit(1)
  64. }
  65. if (initiatorId && !ObjectId.isValid(initiatorId)) {
  66. console.error(`Invalid ObjectId for --initiator-id: ${initiatorId}`)
  67. printUsageAndExit(1)
  68. }
  69. const policies = { [policyName]: policyValue === 'true' }
  70. const auditLogInfo = {
  71. initiatorId: initiatorId ? new ObjectId(initiatorId) : undefined,
  72. ipAddress: '0.0.0.0',
  73. }
  74. let subscriptionIds
  75. if (subscriptionId) {
  76. subscriptionIds = [subscriptionId]
  77. } else {
  78. const fileContent = fs.readFileSync(filePath, 'utf8')
  79. subscriptionIds = fileContent
  80. .split('\n')
  81. .map(line => line.trim())
  82. .filter(line => line.length > 0)
  83. }
  84. const invalidIds = subscriptionIds.filter(id => !ObjectId.isValid(id))
  85. if (invalidIds.length > 0) {
  86. console.error(`Invalid ObjectId: ${invalidIds.join(', ')}`)
  87. printUsageAndExit(1)
  88. }
  89. console.log(
  90. `${dryRun ? 'Dry run: would update' : 'Updating'} policy ${policyName}=${policyValue}. Total subscriptions to update: ${subscriptionIds.length}`
  91. )
  92. const failedSubscriptions = []
  93. for (const id of subscriptionIds) {
  94. if (dryRun) {
  95. if (verbose) {
  96. console.log(` Dry run: would update subscription ${id}`)
  97. }
  98. } else {
  99. if (verbose) {
  100. console.log(` Updating subscription ${id}`)
  101. }
  102. try {
  103. await GroupPoliciesHandler.promises.updateGroupPolicies(
  104. new ObjectId(id),
  105. policies,
  106. auditLogInfo
  107. )
  108. } catch (error) {
  109. console.error(` Subscription ${id} update failed`)
  110. console.error(error)
  111. failedSubscriptions.push(id)
  112. }
  113. }
  114. }
  115. console.log(
  116. `Total subscriptions ${dryRun ? 'found' : 'updated'}: ${subscriptionIds.length - failedSubscriptions.length}`
  117. )
  118. if (failedSubscriptions.length) {
  119. console.log(`Total subscriptions failed: ${failedSubscriptions.length}:`)
  120. failedSubscriptions.forEach(id => console.log(` ${id}`))
  121. }
  122. }
  123. try {
  124. await scriptRunner(main)
  125. console.log('Done.')
  126. process.exit(0)
  127. } catch (error) {
  128. console.error(error)
  129. process.exit(1)
  130. }