2018-05-30 06:29:21 -04:00
|
|
|
logger = require("logger-sharelatex")
|
|
|
|
crypto = require("crypto")
|
2018-06-01 06:23:25 -04:00
|
|
|
async = require("async")
|
2018-05-30 06:29:21 -04:00
|
|
|
|
|
|
|
settings = require("settings-sharelatex")
|
2018-05-31 06:54:50 -04:00
|
|
|
ObjectId = require("mongojs").ObjectId
|
|
|
|
|
|
|
|
TeamInvite = require("../../models/TeamInvite").TeamInvite
|
|
|
|
Subscription = require("../../models/Subscription").Subscription
|
2018-05-30 06:29:21 -04:00
|
|
|
|
2018-06-01 11:50:00 -04:00
|
|
|
UserGetter = require("../User/UserGetter")
|
2018-05-30 06:29:21 -04:00
|
|
|
SubscriptionLocator = require("./SubscriptionLocator")
|
2018-05-31 06:54:50 -04:00
|
|
|
SubscriptionUpdater = require("./SubscriptionUpdater")
|
|
|
|
LimitationsManager = require("./LimitationsManager")
|
|
|
|
|
2018-05-30 06:29:21 -04:00
|
|
|
EmailHandler = require("../Email/EmailHandler")
|
2018-06-11 09:20:35 -04:00
|
|
|
EmailHelper = require("../Helpers/EmailHelper")
|
|
|
|
|
|
|
|
Errors = require "../Errors/Errors"
|
2018-05-30 06:29:21 -04:00
|
|
|
|
|
|
|
module.exports = TeamInvitesHandler =
|
2018-05-31 11:15:47 -04:00
|
|
|
getInvite: (token, callback) ->
|
|
|
|
Subscription.findOne 'teamInvites.token': token, (err, subscription) ->
|
2018-06-01 06:23:25 -04:00
|
|
|
return callback(err) if err?
|
2018-06-11 09:20:35 -04:00
|
|
|
return callback(new Errors.NotFoundError('team not found')) unless subscription?
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
invite = subscription.teamInvites.find (i) -> i.token == token
|
|
|
|
return callback(null, invite, subscription)
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-06-07 10:35:18 -04:00
|
|
|
createInvite: (teamManagerId, email, callback) ->
|
2018-06-11 10:22:42 -04:00
|
|
|
email = EmailHelper.parseEmail(email)
|
|
|
|
return callback(new Error('invalid email')) if !email?
|
2018-06-01 06:23:25 -04:00
|
|
|
logger.log {teamManagerId, email}, "Creating manager team invite"
|
2018-06-01 11:50:00 -04:00
|
|
|
UserGetter.getUser teamManagerId, (error, teamManager) ->
|
2018-05-31 11:15:47 -04:00
|
|
|
return callback(error) if error?
|
2018-05-30 06:29:21 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
SubscriptionLocator.getUsersSubscription teamManagerId, (error, subscription) ->
|
|
|
|
return callback(error) if error?
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
if teamManager.first_name and teamManager.last_name
|
|
|
|
inviterName = "#{teamManager.first_name} #{teamManager.last_name} (#{teamManager.email})"
|
|
|
|
else
|
|
|
|
inviterName = teamManager.email
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-06-11 09:20:35 -04:00
|
|
|
removeLegacyInvite subscription.id, email, (error) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
createInvite(subscription, email, inviterName, callback)
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
createDomainInvite: (user, licence, callback) ->
|
2018-06-11 10:22:42 -04:00
|
|
|
email = EmailHelper.parseEmail(user.email)
|
|
|
|
return callback(new Error('invalid email')) if !email?
|
|
|
|
logger.log {licence, email: email}, "Creating domain team invite"
|
2018-09-05 05:35:25 -04:00
|
|
|
# If name == 'Uni of X License', make the email read only
|
|
|
|
# 'Uni of X has invited you...'
|
|
|
|
inviterName = licence.name.replace(/\s+(site\s+)?licence$/i, '')
|
2018-06-07 10:01:48 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
SubscriptionLocator.getSubscription licence.subscription_id, (error, subscription) ->
|
|
|
|
return callback(error) if error?
|
2018-06-11 10:22:42 -04:00
|
|
|
createInvite(subscription, email, inviterName, callback)
|
2018-05-30 06:29:21 -04:00
|
|
|
|
2018-06-18 09:18:23 -04:00
|
|
|
importInvite: (subscription, inviterName, email, token, sentAt, callback) ->
|
|
|
|
checkIfInviteIsPossible subscription, email, (error, possible, reason) ->
|
|
|
|
return callback(error) if error?
|
|
|
|
return callback(reason) unless possible
|
|
|
|
|
|
|
|
subscription.teamInvites.push({
|
|
|
|
email: email
|
|
|
|
inviterName: inviterName
|
|
|
|
token: token
|
|
|
|
sentAt: sentAt
|
|
|
|
})
|
|
|
|
|
|
|
|
subscription.save callback
|
|
|
|
|
2018-05-31 06:54:50 -04:00
|
|
|
acceptInvite: (token, userId, callback) ->
|
2018-06-01 06:23:25 -04:00
|
|
|
logger.log {userId}, "Accepting invite"
|
2018-05-31 11:15:47 -04:00
|
|
|
TeamInvitesHandler.getInvite token, (err, invite, subscription) ->
|
2018-05-31 06:54:50 -04:00
|
|
|
return callback(err) if err?
|
2018-06-11 09:20:35 -04:00
|
|
|
return callback(new Errors.NotFoundError('invite not found')) unless invite?
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-07-16 09:42:37 -04:00
|
|
|
SubscriptionUpdater.addUserToGroup subscription._id, userId, (err) ->
|
2018-05-31 06:54:50 -04:00
|
|
|
return callback(err) if err?
|
2018-05-30 06:29:21 -04:00
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
removeInviteFromTeam(subscription.id, invite.email, callback)
|
2018-05-30 06:29:21 -04:00
|
|
|
|
2018-05-31 06:54:50 -04:00
|
|
|
revokeInvite: (teamManagerId, email, callback) ->
|
2018-06-11 10:22:42 -04:00
|
|
|
email = EmailHelper.parseEmail(email)
|
|
|
|
return callback(new Error('invalid email')) if !email?
|
2018-06-01 06:23:25 -04:00
|
|
|
logger.log {teamManagerId, email}, "Revoking invite"
|
2018-05-31 06:54:50 -04:00
|
|
|
SubscriptionLocator.getUsersSubscription teamManagerId, (err, teamSubscription) ->
|
|
|
|
return callback(err) if err?
|
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
removeInviteFromTeam(teamSubscription.id, email, callback)
|
2018-05-30 08:06:27 -04:00
|
|
|
|
2018-06-06 07:35:13 -04:00
|
|
|
# Legacy method to allow a user to receive a confirmation email if their
|
|
|
|
# email is in Subscription.invited_emails when they join. We'll remove this
|
|
|
|
# after a short while.
|
|
|
|
createTeamInvitesForLegacyInvitedEmail: (email, callback) ->
|
|
|
|
SubscriptionLocator.getGroupsWithEmailInvite email, (err, teams) ->
|
|
|
|
return callback(err) if err?
|
|
|
|
|
|
|
|
async.map teams,
|
2018-06-07 10:35:18 -04:00
|
|
|
(team, cb) -> TeamInvitesHandler.createInvite(team.admin_id, email, cb)
|
2018-06-06 07:35:13 -04:00
|
|
|
, callback
|
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
createInvite = (subscription, email, inviterName, callback) ->
|
|
|
|
logger.log {subscriptionId: subscription.id, email, inviterName}, "Creating invite"
|
|
|
|
checkIfInviteIsPossible subscription, email, (error, possible, reason) ->
|
2018-06-01 10:37:09 -04:00
|
|
|
return callback(error) if error?
|
|
|
|
return callback(reason) unless possible
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-06-06 10:13:49 -04:00
|
|
|
invite = subscription.teamInvites.find (invite) -> invite.email == email
|
|
|
|
|
|
|
|
if !invite?
|
2018-06-06 12:11:25 -04:00
|
|
|
invite = {
|
|
|
|
email: email
|
|
|
|
inviterName: inviterName
|
|
|
|
token: crypto.randomBytes(32).toString("hex")
|
|
|
|
sentAt: new Date()
|
|
|
|
}
|
2018-06-06 10:13:49 -04:00
|
|
|
subscription.teamInvites.push(invite)
|
2018-06-06 12:11:25 -04:00
|
|
|
else
|
|
|
|
invite.sentAt = new Date()
|
2018-05-31 06:54:50 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
subscription.save (error) ->
|
|
|
|
return callback(error) if error?
|
2018-05-30 08:06:27 -04:00
|
|
|
|
2018-05-31 11:15:47 -04:00
|
|
|
opts =
|
2018-06-06 10:13:49 -04:00
|
|
|
to: email
|
2018-05-31 11:15:47 -04:00
|
|
|
inviterName: inviterName
|
2018-06-06 10:13:49 -04:00
|
|
|
acceptInviteUrl: "#{settings.siteUrl}/subscription/invites/#{invite.token}/"
|
2018-06-06 12:11:25 -04:00
|
|
|
appName: settings.appName
|
2018-05-31 11:15:47 -04:00
|
|
|
EmailHandler.sendEmail "verifyEmailToJoinTeam", opts, (error) ->
|
|
|
|
return callback(error, invite)
|
2018-05-30 08:06:27 -04:00
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
removeInviteFromTeam = (subscriptionId, email, callback) ->
|
|
|
|
searchConditions = { _id: new ObjectId(subscriptionId.toString()) }
|
2018-06-07 11:22:38 -04:00
|
|
|
removeInvite = { $pull: { teamInvites: { email: email } } }
|
2018-06-11 09:20:35 -04:00
|
|
|
logger.log {subscriptionId, email, searchConditions, removeInvite}, 'removeInviteFromTeam'
|
2018-06-07 11:22:38 -04:00
|
|
|
|
|
|
|
async.series [
|
|
|
|
(cb) -> Subscription.update(searchConditions, removeInvite, cb),
|
2018-06-11 09:20:35 -04:00
|
|
|
(cb) -> removeLegacyInvite(subscriptionId, email, cb),
|
2018-06-07 11:22:38 -04:00
|
|
|
], callback
|
|
|
|
|
2018-06-11 09:20:35 -04:00
|
|
|
removeLegacyInvite = (subscriptionId, email, callback) ->
|
|
|
|
Subscription.update({
|
|
|
|
_id: new ObjectId(subscriptionId.toString())
|
|
|
|
}, {
|
|
|
|
$pull: {
|
2018-06-11 10:22:42 -04:00
|
|
|
invited_emails: email
|
2018-06-11 09:20:35 -04:00
|
|
|
}
|
|
|
|
}, callback)
|
2018-06-01 06:23:25 -04:00
|
|
|
|
|
|
|
checkIfInviteIsPossible = (subscription, email, callback = (error, possible, reason) -> ) ->
|
2018-06-08 04:58:51 -04:00
|
|
|
unless subscription.groupPlan
|
|
|
|
logger.log {subscriptionId: subscription.id},
|
|
|
|
"can not add members to a subscription that is not in a group plan"
|
|
|
|
return callback(null, false, wrongPlan: true)
|
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
if LimitationsManager.teamHasReachedMemberLimit(subscription)
|
|
|
|
logger.log {subscriptionId: subscription.id}, "team has reached member limit"
|
|
|
|
return callback(null, false, limitReached: true)
|
|
|
|
|
2018-06-07 07:49:46 -04:00
|
|
|
UserGetter.getUserByAnyEmail email, (error, existingUser) ->
|
2018-06-01 06:23:25 -04:00
|
|
|
return callback(error) if error?
|
2018-06-07 07:49:46 -04:00
|
|
|
return callback(null, true) unless existingUser?
|
2018-06-01 06:23:25 -04:00
|
|
|
|
2018-06-07 07:49:46 -04:00
|
|
|
existingMember = subscription.member_ids.find (memberId) ->
|
|
|
|
memberId.toString() == existingUser._id.toString()
|
2018-05-30 08:06:27 -04:00
|
|
|
|
2018-06-01 06:23:25 -04:00
|
|
|
if existingMember
|
|
|
|
logger.log {subscriptionId: subscription.id, email}, "user already in team"
|
|
|
|
return callback(null, false, alreadyInTeam: true)
|
|
|
|
else
|
|
|
|
return callback(null, true)
|