From 830d0daa38278adcbbc73e398ce6948860fa8589 Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Wed, 2 Apr 2025 12:26:55 +0200 Subject: [PATCH] Script for removing user enrollment from a subscription (#24627) * Script for removing user enrollment to a subcription * Add "release-managed-user" audit log event for an user id GitOrigin-RevId: adf2dd97ac82977bcfa07d9a24d1f3c190d095a2 --- .../src/Features/User/UserAuditLogHandler.js | 2 + .../web/scripts/remove_user_enrollment.mjs | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 services/web/scripts/remove_user_enrollment.mjs diff --git a/services/web/app/src/Features/User/UserAuditLogHandler.js b/services/web/app/src/Features/User/UserAuditLogHandler.js index 6984c4f034..b1d404303e 100644 --- a/services/web/app/src/Features/User/UserAuditLogHandler.js +++ b/services/web/app/src/Features/User/UserAuditLogHandler.js @@ -7,6 +7,7 @@ function _canHaveNoIpAddressId(operation, info) { if (operation === 'leave-group-subscription') return true if (operation === 'must-reset-password-set') return true if (operation === 'remove-email' && info.script) return true + if (operation === 'release-managed-user' && info.script) return true return false } @@ -22,6 +23,7 @@ function _canHaveNoInitiatorId(operation, info) { if (operation === 'must-reset-password-set') return true if (operation === 'must-reset-password-unset') return true if (operation === 'account-suspension' && info.script) return true + if (operation === 'release-managed-user' && info.script) return true } /** diff --git a/services/web/scripts/remove_user_enrollment.mjs b/services/web/scripts/remove_user_enrollment.mjs new file mode 100644 index 0000000000..e3b80b6090 --- /dev/null +++ b/services/web/scripts/remove_user_enrollment.mjs @@ -0,0 +1,93 @@ +// This script will: +// 1. remove enrollment.managedBy and enrollment.enrolledAt fields from the User record +// 2. remove the user_id from member_ids on the Subscription record +// +// Usage: +// $ node scripts/remove_user_enrollment.mjs --id USER_ID --commit + +import minimist from 'minimist' +import { ObjectId } from '../app/src/infrastructure/mongodb.js' +import { User } from '../app/src/models/User.js' +import { Subscription } from '../app/src/models/Subscription.js' +import UserAuditLogHandler from '../app/src/Features/User/UserAuditLogHandler.js' + +const argv = minimist(process.argv.slice(2)) +const COMMIT = argv.commit !== undefined +if (!COMMIT) { + console.warn('Doing dry run without --commit') +} + +const userId = argv.id +if (!userId) throw new Error('missing user ID (use --id)') + +async function _handleUser(userId) { + const user = await User.findById(userId, { enrollment: 1 }) + if (!user) { + throw new Error(`user ${userId} does not exist`) + } + + if (!user.enrollment?.managedBy) { + throw new Error(`user ${userId} has no enrollment`) + } + + const subscriptionId = user.enrollment.managedBy + + if (COMMIT) { + await Subscription.updateOne( + { _id: subscriptionId }, + { $pull: { member_ids: userId } } + ) + } else { + console.log( + `Would remove user ${userId} from subscription ${subscriptionId}` + ) + } + + if (COMMIT) { + await User.updateOne( + { _id: userId }, + { + $unset: { + 'enrollment.managedBy': 1, + 'enrollment.enrolledAt': 1, + }, + } + ) + } else { + console.log(`Would remove enrollment from user ${userId}`) + } + + if (COMMIT) { + await UserAuditLogHandler.promises.addEntry( + userId, + 'release-managed-user', + undefined, + undefined, + { script: true, subscriptionId, comment: 'removed by support' } + ) + } else { + console.log( + `Would create user audit log "release-managed-user" with comment "removed by support"` + ) + } +} + +async function processUser(userId) { + console.log('---Starting remove enrollment script---') + console.log(`Will process user: ${userId}`) + + if (!ObjectId.isValid(userId)) { + throw new Error(`user ID not valid: ${userId}`) + } + + try { + await _handleUser(new ObjectId(userId)) + console.log(`User ${userId} processed successfully`) + } catch (error) { + console.log(`Failed to process user ${userId}:`, error) + } + + process.exit() +} + +await processUser(userId)