Files
overleaf-cep/services/web/app/src/Features/Subscription/TeamInvitesController.js
T
M Fahru a834e02cd5 Merge pull request #14442 from overleaf/mf-resend-group-invite
[web] Add an option to resend group invite in managed users setting

GitOrigin-RevId: 75625c5a50dfc74b48b3a465c9f713e2d6179db8
2023-08-24 08:04:18 +00:00

241 lines
6.9 KiB
JavaScript

const settings = require('@overleaf/settings')
const logger = require('@overleaf/logger')
const OError = require('@overleaf/o-error')
const TeamInvitesHandler = require('./TeamInvitesHandler')
const SessionManager = require('../Authentication/SessionManager')
const SubscriptionLocator = require('./SubscriptionLocator')
const ErrorController = require('../Errors/ErrorController')
const EmailHelper = require('../Helpers/EmailHelper')
const UserGetter = require('../User/UserGetter')
const { expressify } = require('../../util/promises')
const HttpErrorHandler = require('../Errors/HttpErrorHandler')
const PermissionsManager = require('../Authorization/PermissionsManager')
const EmailHandler = require('../Email/EmailHandler')
const { RateLimiter } = require('../../infrastructure/RateLimiter')
const rateLimiters = {
resendGroupInvite: new RateLimiter('resend-group-invite', {
points: 1,
duration: 60 * 60,
}),
}
async function createInvite(req, res, next) {
const teamManagerId = SessionManager.getLoggedInUserId(req.session)
const subscription = req.entity
const email = EmailHelper.parseEmail(req.body.email)
if (!email) {
return res.status(422).json({
error: {
code: 'invalid_email',
message: req.i18n.translate('invalid_email'),
},
})
}
try {
const invitedUserData = await TeamInvitesHandler.promises.createInvite(
teamManagerId,
subscription,
email
)
return res.json({ user: invitedUserData })
} catch (err) {
if (err.alreadyInTeam) {
return res.status(400).json({
error: {
code: 'user_already_added',
message: req.i18n.translate('user_already_added'),
},
})
}
if (err.limitReached) {
return res.status(400).json({
error: {
code: 'group_full',
message: req.i18n.translate('group_full'),
},
})
}
}
}
async function viewInvite(req, res, next) {
const { token } = req.params
const userId = SessionManager.getLoggedInUserId(req.session)
const { invite, subscription } = await TeamInvitesHandler.promises.getInvite(
token
)
if (!invite) {
return ErrorController.notFound(req, res)
}
let validationStatus = new Map()
if (userId) {
const personalSubscription =
await SubscriptionLocator.promises.getUsersSubscription(userId)
const hasIndividualRecurlySubscription =
personalSubscription &&
personalSubscription.groupPlan === false &&
personalSubscription.recurlyStatus?.state !== 'canceled' &&
personalSubscription.recurlySubscription_id &&
personalSubscription.recurlySubscription_id !== ''
if (subscription?.groupPolicy) {
if (!subscription.populated('groupPolicy')) {
await subscription.populate('groupPolicy')
}
const user = await UserGetter.promises.getUser(userId)
if (
user.enrollment?.managedBy &&
user.enrollment?.managedBy.toString() !== subscription._id.toString()
) {
return HttpErrorHandler.forbidden(
req,
res,
'User is already managed by a different subscription'
)
}
validationStatus =
await PermissionsManager.promises.getUserValidationStatus({
user,
groupPolicy: subscription.groupPolicy,
subscription,
})
let currentManagedUserAdminEmail
try {
currentManagedUserAdminEmail =
await SubscriptionLocator.promises.getAdminEmail(subscription._id)
} catch (err) {
logger.error({ err }, 'error getting subscription admin email')
}
return res.render('subscriptions/team/invite-managed', {
inviterName: invite.inviterName,
inviteToken: invite.token,
expired: req.query.expired,
validationStatus: Object.fromEntries(validationStatus),
currentManagedUserAdminEmail,
})
} else {
let currentManagedUserAdminEmail
try {
currentManagedUserAdminEmail =
await SubscriptionLocator.promises.getAdminEmail(req.managedBy)
} catch (err) {
logger.error({ err }, 'error getting subscription admin email')
}
return res.render('subscriptions/team/invite', {
inviterName: invite.inviterName,
inviteToken: invite.token,
hasIndividualRecurlySubscription,
appName: settings.appName,
expired: req.query.expired,
userRestrictions: Array.from(req.userRestrictions || []),
currentManagedUserAdminEmail,
})
}
} else {
const userByEmail = await UserGetter.promises.getUserByMainEmail(
invite.email
)
return res.render('subscriptions/team/invite_logged_out', {
inviterName: invite.inviterName,
inviteToken: invite.token,
appName: settings.appName,
accountExists: userByEmail != null,
emailAddress: invite.email,
})
}
}
async function acceptInvite(req, res, next) {
const { token } = req.params
const userId = SessionManager.getLoggedInUserId(req.session)
await TeamInvitesHandler.promises.acceptInvite(token, userId)
res.sendStatus(204)
}
function revokeInvite(req, res, next) {
const subscription = req.entity
const email = EmailHelper.parseEmail(req.params.email)
const teamManagerId = SessionManager.getLoggedInUserId(req.session)
if (!email) {
return res.sendStatus(400)
}
TeamInvitesHandler.revokeInvite(
teamManagerId,
subscription,
email,
function (err, results) {
if (err) {
return next(err)
}
res.sendStatus(204)
}
)
}
async function resendInvite(req, res, next) {
const { entity: subscription } = req
const userEmail = EmailHelper.parseEmail(req.body.email)
await subscription.populate('admin_id', ['email', 'first_name', 'last_name'])
if (!userEmail) {
throw new Error('invalid email')
}
const currentInvite = subscription.teamInvites.find(
invite => invite?.email === userEmail
)
if (!currentInvite) {
return await createInvite(req, res)
}
const opts = {
to: userEmail,
admin: subscription.admin_id,
inviter: currentInvite.inviterName,
acceptInviteUrl: `${settings.siteUrl}/subscription/invites/${currentInvite.token}/`,
reminder: true,
}
try {
await rateLimiters.resendGroupInvite.consume(userEmail)
const existingUser = await UserGetter.promises.getUserByAnyEmail(userEmail)
const emailTemplate = existingUser
? 'verifyEmailToJoinManagedUsers'
: 'inviteNewUserToJoinManagedUsers'
EmailHandler.sendDeferredEmail(emailTemplate, opts)
} catch (err) {
if (err?.remainingPoints === 0) {
return res.sendStatus(429)
} else {
throw OError.tag(err, 'Failed to resend group invite email')
}
}
return res.status(200).json({ success: true })
}
module.exports = {
createInvite: expressify(createInvite),
viewInvite: expressify(viewInvite),
acceptInvite: expressify(acceptInvite),
revokeInvite,
resendInvite: expressify(resendInvite),
}