overleaf/services/web/app/src/Features/User/UserDeleter.js

142 lines
4.3 KiB
JavaScript
Raw Normal View History

const { User } = require('../../models/User')
const { DeletedUser } = require('../../models/DeletedUser')
const NewsletterManager = require('../Newsletter/NewsletterManager')
const ProjectDeleterPromises = require('../Project/ProjectDeleter').promises
const logger = require('logger-sharelatex')
const moment = require('moment')
const SubscriptionHandler = require('../Subscription/SubscriptionHandler')
const SubscriptionUpdater = require('../Subscription/SubscriptionUpdater')
const SubscriptionLocator = require('../Subscription/SubscriptionLocator')
const UserMembershipsHandler = require('../UserMembership/UserMembershipsHandler')
const async = require('async')
const InstitutionsAPI = require('../Institutions/InstitutionsAPI')
const Errors = require('../Errors/Errors')
const { promisify, callbackify } = require('util')
let UserDeleter
module.exports = UserDeleter = {
deleteUser: callbackify(deleteUser),
expireDeletedUser: callbackify(expireDeletedUser),
ensureCanDeleteUser: callbackify(ensureCanDeleteUser),
expireDeletedUsersAfterDuration: callbackify(expireDeletedUsersAfterDuration),
promises: {
deleteUser: deleteUser,
expireDeletedUser: expireDeletedUser,
ensureCanDeleteUser: ensureCanDeleteUser,
expireDeletedUsersAfterDuration: expireDeletedUsersAfterDuration
}
}
async function deleteUser(userId, options = {}) {
if (!userId) {
logger.warn('user_id is null when trying to delete user')
throw new Error('no user_id')
}
try {
let user = await User.findById(userId).exec()
logger.log({ user }, 'deleting user')
await UserDeleter.promises.ensureCanDeleteUser(user)
await _createDeletedUser(user, options)
await _cleanupUser(user)
await ProjectDeleterPromises.deleteUsersProjects(user._id)
await User.deleteOne({ _id: userId }).exec()
} catch (error) {
logger.warn({ error, userId }, 'something went wrong deleting the user')
throw error
}
}
async function expireDeletedUser(userId) {
let deletedUser = await DeletedUser.findOne({
'deleterData.deletedUserId': userId
}).exec()
deletedUser.user = undefined
deletedUser.deleterData.deleterIpAddress = undefined
await deletedUser.save()
}
async function expireDeletedUsersAfterDuration() {
const DURATION = 90
let deletedUsers = await DeletedUser.find({
'deleterData.deletedAt': {
$lt: new Date(moment().subtract(DURATION, 'days'))
},
user: {
$ne: null
}
}).exec()
if (deletedUsers.length === 0) {
logger.log('No deleted users were found for duration')
return
}
for (let i = 0; i < deletedUsers.length; i++) {
await UserDeleter.promises.expireDeletedUser(
deletedUsers[i].deleterData.deletedUserId
)
}
}
async function ensureCanDeleteUser(user) {
await new Promise((resolve, reject) => {
SubscriptionLocator.getUsersSubscription(user, (error, subscription) => {
if (error) {
return reject(error)
}
if (subscription) {
return reject(new Errors.SubscriptionAdminDeletionError())
}
resolve()
})
})
}
async function _createDeletedUser(user, options) {
await DeletedUser.create({
user: user,
deleterData: {
deletedAt: new Date(),
deleterId: options.deleterUser ? options.deleterUser._id : undefined,
deleterIpAddress: options.ipAddress,
deletedUserId: user._id,
deletedUserLastLoggedIn: user.lastLoggedIn,
deletedUserSignUpDate: user.signUpDate,
deletedUserLoginCount: user.loginCount,
deletedUserReferralId: user.referal_id,
deletedUserReferredUsers: user.refered_users,
deletedUserReferredUserCount: user.refered_user_count,
deletedUserOverleafId: user.overleaf ? user.overleaf.id : undefined
}
})
}
async function _cleanupUser(user) {
if (user == null) {
throw new Error('no user supplied')
}
const runInSeries = promisify(async.series)
await runInSeries([
cb =>
NewsletterManager.unsubscribe(user, err => {
logger.err('Failed to unsubscribe user from newsletter', {
user_id: user._id,
error: err
})
cb()
}),
cb => SubscriptionHandler.cancelSubscription(user, cb),
cb => InstitutionsAPI.deleteAffiliations(user._id, cb),
cb => SubscriptionUpdater.removeUserFromAllGroups(user._id, cb),
cb => UserMembershipsHandler.removeUserFromAllEntities(user._id, cb)
])
}