Files
overleaf-cep/services/web/app/src/Features/Subscription/ManagedUsersHandler.js
M Fahru 9cb017487a Fix invite url is not passed correctly to managed users invitation emails
GitOrigin-RevId: f334215a7c83088df175467561d41537cfe9e2a8
2023-09-14 08:04:41 +00:00

208 lines
6.7 KiB
JavaScript

const { callbackify } = require('util')
const { Subscription } = require('../../models/Subscription')
const { GroupPolicy } = require('../../models/GroupPolicy')
const { User } = require('../../models/User')
const ManagedUsersPolicy = require('./ManagedUsersPolicy')
const OError = require('@overleaf/o-error')
const settings = require('@overleaf/settings')
const {
UserNotFoundError,
SubscriptionNotFoundError,
} = require('../Errors/Errors')
const UserGetter = require('../User/UserGetter')
const UserUpdater = require('../User/UserUpdater')
const EmailHandler = require('../Email/EmailHandler')
const logger = require('@overleaf/logger')
/**
* This module contains functions for handling managed users in a
* group subscription.
*/
/**
* Enables managed users for a given subscription by creating a new
* group policy with default settings for managed users and updating
* the subscription to use the new policy.
* @async
* @function
* @param {string} subscriptionId - The ID of the subscription to enable
* managed users for.
* @returns {Promise<void>} - A Promise that resolves when the subscription
* has been updated with the new group policy.
*/
async function enableManagedUsers(subscriptionId) {
const subscription = await Subscription.findById(subscriptionId).exec()
// create a new Group policy with the default settings for managed users
const policy = ManagedUsersPolicy.getDefaultPolicy()
const groupPolicy = new GroupPolicy(policy)
await groupPolicy.save()
// update the subscription to use the new policy
subscription.groupPolicy = groupPolicy._id
await subscription.save()
await _sendEmailToGroupMembers(subscriptionId)
}
/**
* Disables managed users for a given subscription by removing the
* group policy and deleting enrolment information for all managed users.
* @async
* @function
* @param {string} subscriptionId - The ID of the subscription to disable
* managed users for.
* @returns {Promise<void>} - A Promise that resolves when the subscription and
* users have been updated.
*/
async function disableManagedUsers(subscriptionId) {
const subscription = await Subscription.findById(subscriptionId).exec()
for (const userId of subscription.member_ids || []) {
const user = await UserGetter.promises.getUser(userId, { enrollment: 1 })
if (
user.enrollment?.managedBy?.toString() === subscription._id.toString()
) {
await UserUpdater.promises.updateUser(userId, {
$unset: { enrollment: 1 },
})
}
}
subscription.groupPolicy = undefined
await subscription.save()
}
/**
* Retrieves the group policy for a user enrolled in a managed group.
* @async
* @function
* @param {Object} requestedUser - The user object to retrieve the group policy for.
* @returns {Promise<Object>} - A Promise that resolves with the group policy
* and subscription objects for the user's enrollment, or null if it does not exist.
*/
async function getEnrollmentForUser(requestedUser) {
// Don't rely on the user being populated, it may be a session user without
// the enrollment property. Force the user to be loaded from mongo.
const user = await User.findById(requestedUser._id, 'enrollment')
if (!user) {
throw new UserNotFoundError({ info: { userId: requestedUser._id } })
}
// Now we are sure the user exists and we have the full contents
if (user.enrollment?.managedBy == null) {
return {}
}
// retrieve the subscription and the group policy (without the _id field)
const subscription = await Subscription.findById(user.enrollment.managedBy)
.populate('groupPolicy', '-_id')
.exec()
if (!subscription) {
throw new SubscriptionNotFoundError({
info: { subscriptionId: user.enrollment.managedBy, userId: user._id },
})
}
// check whether the user is an admin of the subscription
const isManagedGroupAdmin = user._id.equals(subscription.admin_id)
// return the group policy as a plain object (without the __v field)
const groupPolicy = subscription.groupPolicy.toObject({
versionKey: false,
})
return {
groupPolicy,
managedBy: user.enrollment.managedBy,
isManagedGroupAdmin,
}
}
async function enrollInSubscription(userId, subscription) {
// check whether the user is already enrolled in a subscription
const user = await User.findOne(
{
_id: userId,
'enrollment.managedBy': { $exists: true },
},
{ _id: 1 }
).exec()
if (user != null) {
throw new OError('User is already enrolled in a subscription', {
userId,
subscriptionId: subscription._id,
})
}
// update the user to be enrolled in the subscription
const updatedUser = await User.findOneAndUpdate(
{ _id: userId, 'enrollment.managedBy': { $exists: false } },
{
enrollment: {
managedBy: subscription._id,
enrolledAt: new Date(),
},
},
{ new: true }
).exec()
// check whether the enrollment succeeded
if (
!updatedUser ||
!subscription.equals(updatedUser?.enrollment?.managedBy)
) {
throw new OError('Failed to enroll user in subscription', {
userId,
subscriptionId: subscription._id,
})
}
}
/**
* Send email to all group members, irregardless of the member status.
* @async
* @function
* @param {string} subscriptionId - The ID of the subscription to enable
* managed users for.
* @returns {Promise<void>} - A Promise that resolves when all the `sendEmail` function has been sent,
* irregardless of whether they're successful or failed.
*/
async function _sendEmailToGroupMembers(subscriptionId) {
const EMAIL_DELAY_IN_MS = 0
const subscription = await Subscription.findById(subscriptionId)
.populate('member_ids', 'email')
.populate('admin_id', ['first_name', 'last_name', 'email'])
.exec()
// On failure, log the error and carry on so that one email failing does not prevent other emails sending
for (const recipient of subscription.member_ids) {
try {
const opts = {
to: recipient.email,
admin: subscription.admin_id,
groupName: subscription.teamName,
acceptInviteUrl: `${settings.siteUrl}/subscription/${subscriptionId}/enrollment/`,
}
EmailHandler.sendDeferredEmail(
'surrenderAccountForManagedUsers',
opts,
EMAIL_DELAY_IN_MS
)
} catch (err) {
logger.error(
{ err, userId: recipient._id },
'could not send notification email to surrender account'
)
}
}
}
module.exports = {
promises: {
enableManagedUsers,
disableManagedUsers,
getEnrollmentForUser,
enrollInSubscription,
},
enableManagedUsers: callbackify(enableManagedUsers),
getEnrollmentForUser: callbackify(getEnrollmentForUser),
enrollInSubscription: callbackify(enrollInSubscription),
disableManagedUsers: callbackify(disableManagedUsers),
}