Files
overleaf-cep/services/web/scripts/update_group_policy.mjs
Miguel Serrano 08701ca5aa [web] Add update_group_policy script (#31715)
* [web]  Add `update_group_policy` script

The script allows to update a policy for a subscription, or update
multiple policies via a file containing a list of subscription ids

GitOrigin-RevId: f3b3a6666eb62bb93d13c4c75d60b99f21f23db4
2026-04-24 08:06:35 +00:00

146 lines
4.5 KiB
JavaScript

import fs from 'node:fs'
import minimist from 'minimist'
import { ObjectId } from '../app/src/infrastructure/mongodb.mjs'
import GroupPoliciesHandler from '../modules/group-settings/app/src/GroupPoliciesHandler.mjs'
import { scriptRunner } from './lib/ScriptRunner.mjs'
// Imported to ensure policies are registered before we attempt to update them
import '../modules/group-settings/app/src/managed-users/ManagedUsersPolicy.mjs'
import '../modules/institutions/app/src/InstitutionsPolicy.mjs'
const USAGE = `Usage:
node scripts/update_group_policy.mjs --subscription-id=<id> --policy-name=<name> --policy-value=<true|false>
node scripts/update_group_policy.mjs --file=<path> --policy-name=<name> --policy-value=<true|false>
Options:
--subscription-id The id of a single subscription to update
--file Path to a file with one subscription _id per line (ignored when --subscription-id is present)
--policy-name The policy name to update (e.g. userCannotUseAIFeatures)
--policy-value The policy value (true or false)
--dry-run Perform the search but only log the count of subscriptions to update. Defaults to true
--initiator-id The user id of the person running the script (for audit logs)
--verbose Log each subscription id being updated
--help Show this help message`
function printUsageAndExit(code) {
console.log(USAGE)
process.exit(code)
}
const argv = minimist(process.argv.slice(2), {
string: [
'subscription-id',
'policy-name',
'policy-value',
'initiator-id',
'file',
],
boolean: ['dry-run', 'help', 'verbose'],
default: { 'dry-run': true, verbose: false },
})
if (argv.help) {
printUsageAndExit(0)
}
async function main() {
const subscriptionId = argv['subscription-id']
const policyName = argv['policy-name']
const policyValue = argv['policy-value']
const filePath = argv.file
const dryRun = argv['dry-run']
const initiatorId = argv['initiator-id']
const verbose = argv.verbose
if (!policyName) {
console.error('Missing --policy-name argument')
printUsageAndExit(1)
}
if (!subscriptionId && !filePath) {
console.error(
'Missing --subscription-id or --file argument, one must be provided'
)
printUsageAndExit(1)
}
if (!policyValue) {
console.error('Missing --policy-value argument')
printUsageAndExit(1)
}
if (policyValue !== 'true' && policyValue !== 'false') {
console.error('--policy-value must be "true" or "false"')
printUsageAndExit(1)
}
if (initiatorId && !ObjectId.isValid(initiatorId)) {
console.error(`Invalid ObjectId for --initiator-id: ${initiatorId}`)
printUsageAndExit(1)
}
const policies = { [policyName]: policyValue === 'true' }
const auditLogInfo = {
initiatorId: initiatorId ? new ObjectId(initiatorId) : undefined,
ipAddress: '0.0.0.0',
}
let subscriptionIds
if (subscriptionId) {
subscriptionIds = [subscriptionId]
} else {
const fileContent = fs.readFileSync(filePath, 'utf8')
subscriptionIds = fileContent
.split('\n')
.map(line => line.trim())
.filter(line => line.length > 0)
}
const invalidIds = subscriptionIds.filter(id => !ObjectId.isValid(id))
if (invalidIds.length > 0) {
console.error(`Invalid ObjectId: ${invalidIds.join(', ')}`)
printUsageAndExit(1)
}
console.log(
`${dryRun ? 'Dry run: would update' : 'Updating'} policy ${policyName}=${policyValue}. Total subscriptions to update: ${subscriptionIds.length}`
)
const failedSubscriptions = []
for (const id of subscriptionIds) {
if (dryRun) {
if (verbose) {
console.log(` Dry run: would update subscription ${id}`)
}
} else {
if (verbose) {
console.log(` Updating subscription ${id}`)
}
try {
await GroupPoliciesHandler.promises.updateGroupPolicies(
new ObjectId(id),
policies,
auditLogInfo
)
} catch (error) {
console.error(` Subscription ${id} update failed`)
console.error(error)
failedSubscriptions.push(id)
}
}
}
console.log(
`Total subscriptions ${dryRun ? 'found' : 'updated'}: ${subscriptionIds.length - failedSubscriptions.length}`
)
if (failedSubscriptions.length) {
console.log(`Total subscriptions failed: ${failedSubscriptions.length}:`)
failedSubscriptions.forEach(id => console.log(` ${id}`))
}
}
try {
await scriptRunner(main)
console.log('Done.')
process.exit(0)
} catch (error) {
console.error(error)
process.exit(1)
}