|
@@ -1,5 +1,4 @@
|
|
|
-let CollaboratorsInviteController
|
|
|
|
|
-const OError = require('@overleaf/o-error')
|
|
|
|
|
|
|
+const { callbackify } = require('util')
|
|
|
const ProjectGetter = require('../Project/ProjectGetter')
|
|
const ProjectGetter = require('../Project/ProjectGetter')
|
|
|
const LimitationsManager = require('../Subscription/LimitationsManager')
|
|
const LimitationsManager = require('../Subscription/LimitationsManager')
|
|
|
const UserGetter = require('../User/UserGetter')
|
|
const UserGetter = require('../User/UserGetter')
|
|
@@ -12,6 +11,7 @@ const EditorRealTimeController = require('../Editor/EditorRealTimeController')
|
|
|
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
|
const AnalyticsManager = require('../Analytics/AnalyticsManager')
|
|
|
const SessionManager = require('../Authentication/SessionManager')
|
|
const SessionManager = require('../Authentication/SessionManager')
|
|
|
const { RateLimiter } = require('../../infrastructure/RateLimiter')
|
|
const { RateLimiter } = require('../../infrastructure/RateLimiter')
|
|
|
|
|
+const { expressify } = require('../../util/promises')
|
|
|
|
|
|
|
|
// This rate limiter allows a different number of requests depending on the
|
|
// This rate limiter allows a different number of requests depending on the
|
|
|
// number of callaborators a user is allowed. This is implemented by providing
|
|
// number of callaborators a user is allowed. This is implemented by providing
|
|
@@ -28,76 +28,62 @@ const rateLimiter = new RateLimiter('invite-to-project-by-user-id', {
|
|
|
duration: 60 * 30,
|
|
duration: 60 * 30,
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-module.exports = CollaboratorsInviteController = {
|
|
|
|
|
- getAllInvites(req, res, next) {
|
|
|
|
|
|
|
+const CollaboratorsInviteController = {
|
|
|
|
|
+ async getAllInvites(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
logger.debug({ projectId }, 'getting all active invites for project')
|
|
logger.debug({ projectId }, 'getting all active invites for project')
|
|
|
- CollaboratorsInviteHandler.getAllInvites(
|
|
|
|
|
- projectId,
|
|
|
|
|
- function (err, invites) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error getting invites for project', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- res.json({ invites })
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const invites = await CollaboratorsInviteHandler.promises.getAllInvites(
|
|
|
|
|
+ projectId
|
|
|
)
|
|
)
|
|
|
|
|
+ res.json({ invites })
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- _checkShouldInviteEmail(email, callback) {
|
|
|
|
|
|
|
+ async _checkShouldInviteEmail(email) {
|
|
|
if (Settings.restrictInvitesToExistingAccounts === true) {
|
|
if (Settings.restrictInvitesToExistingAccounts === true) {
|
|
|
logger.debug({ email }, 'checking if user exists with this email')
|
|
logger.debug({ email }, 'checking if user exists with this email')
|
|
|
- UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (err, user) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- return callback(err)
|
|
|
|
|
- }
|
|
|
|
|
- const userExists = user?._id != null
|
|
|
|
|
- callback(null, userExists)
|
|
|
|
|
|
|
+ const user = await UserGetter.promises.getUserByAnyEmail(email, {
|
|
|
|
|
+ _id: 1,
|
|
|
})
|
|
})
|
|
|
|
|
+ const userExists = user?._id != null
|
|
|
|
|
+ return userExists
|
|
|
} else {
|
|
} else {
|
|
|
- callback(null, true)
|
|
|
|
|
|
|
+ return true
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- _checkRateLimit(userId, callback) {
|
|
|
|
|
- LimitationsManager.allowedNumberOfCollaboratorsForUser(
|
|
|
|
|
- userId,
|
|
|
|
|
- (err, collabLimit) => {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- return callback(err)
|
|
|
|
|
- }
|
|
|
|
|
- if (collabLimit == null || collabLimit === 0) {
|
|
|
|
|
- collabLimit = 1
|
|
|
|
|
- } else if (collabLimit < 0 || collabLimit > 20) {
|
|
|
|
|
- collabLimit = 20
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Consume enough points to hit the rate limit at 10 * collabLimit
|
|
|
|
|
- const maxRequests = 10 * collabLimit
|
|
|
|
|
- const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
|
|
|
|
|
- rateLimiter
|
|
|
|
|
- .consume(userId, points)
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- callback(null, true)
|
|
|
|
|
- })
|
|
|
|
|
- .catch(err => {
|
|
|
|
|
- if (err instanceof Error) {
|
|
|
|
|
- callback(err)
|
|
|
|
|
- } else {
|
|
|
|
|
- callback(null, false)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ async _checkRateLimit(userId) {
|
|
|
|
|
+ let collabLimit =
|
|
|
|
|
+ await LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
|
|
|
|
|
+ userId
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if (collabLimit == null || collabLimit === 0) {
|
|
|
|
|
+ collabLimit = 1
|
|
|
|
|
+ } else if (collabLimit < 0 || collabLimit > 20) {
|
|
|
|
|
+ collabLimit = 20
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Consume enough points to hit the rate limit at 10 * collabLimit
|
|
|
|
|
+ const maxRequests = 10 * collabLimit
|
|
|
|
|
+ const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
|
|
|
|
|
+ try {
|
|
|
|
|
+ await rateLimiter.consume(userId, points)
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ if (err instanceof Error) {
|
|
|
|
|
+ throw err
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
- )
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- inviteToProject(req, res, next) {
|
|
|
|
|
|
|
+ async inviteToProject(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
- let { email } = req.body
|
|
|
|
|
|
|
+ let { email, privileges } = req.body
|
|
|
const sendingUser = SessionManager.getSessionUser(req.session)
|
|
const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
const sendingUserId = sendingUser._id
|
|
const sendingUserId = sendingUser._id
|
|
|
|
|
+
|
|
|
if (email === sendingUser.email) {
|
|
if (email === sendingUser.email) {
|
|
|
logger.debug(
|
|
logger.debug(
|
|
|
{ projectId, email, sendingUserId },
|
|
{ projectId, email, sendingUserId },
|
|
@@ -105,243 +91,163 @@ module.exports = CollaboratorsInviteController = {
|
|
|
)
|
|
)
|
|
|
return res.json({ invite: null, error: 'cannot_invite_self' })
|
|
return res.json({ invite: null, error: 'cannot_invite_self' })
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
|
|
logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
|
|
|
- LimitationsManager.canAddXCollaborators(projectId, 1, (error, allowed) => {
|
|
|
|
|
- let privileges
|
|
|
|
|
- if (error) {
|
|
|
|
|
- return next(error)
|
|
|
|
|
- }
|
|
|
|
|
- if (!allowed) {
|
|
|
|
|
- logger.debug(
|
|
|
|
|
- { projectId, email, sendingUserId },
|
|
|
|
|
- 'not allowed to invite more users to project'
|
|
|
|
|
- )
|
|
|
|
|
- return res.json({ invite: null })
|
|
|
|
|
- }
|
|
|
|
|
- ;({ email, privileges } = req.body)
|
|
|
|
|
- email = EmailHelper.parseEmail(email, true)
|
|
|
|
|
- if (email == null || email === '') {
|
|
|
|
|
- logger.debug(
|
|
|
|
|
- { projectId, email, sendingUserId },
|
|
|
|
|
- 'invalid email address'
|
|
|
|
|
- )
|
|
|
|
|
- return res.status(400).json({ errorReason: 'invalid_email' })
|
|
|
|
|
- }
|
|
|
|
|
- CollaboratorsInviteController._checkRateLimit(
|
|
|
|
|
- sendingUserId,
|
|
|
|
|
- function (error, underRateLimit) {
|
|
|
|
|
- if (error) {
|
|
|
|
|
- return next(error)
|
|
|
|
|
- }
|
|
|
|
|
- if (!underRateLimit) {
|
|
|
|
|
- return res.sendStatus(429)
|
|
|
|
|
- }
|
|
|
|
|
- CollaboratorsInviteController._checkShouldInviteEmail(
|
|
|
|
|
- email,
|
|
|
|
|
- function (err, shouldAllowInvite) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(
|
|
|
|
|
- err,
|
|
|
|
|
- 'error checking if we can invite this email address',
|
|
|
|
|
- {
|
|
|
|
|
- email,
|
|
|
|
|
- projectId,
|
|
|
|
|
- sendingUserId,
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- if (!shouldAllowInvite) {
|
|
|
|
|
- logger.debug(
|
|
|
|
|
- { email, projectId, sendingUserId },
|
|
|
|
|
- 'not allowed to send an invite to this email address'
|
|
|
|
|
- )
|
|
|
|
|
- return res.json({
|
|
|
|
|
- invite: null,
|
|
|
|
|
- error: 'cannot_invite_non_user',
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- CollaboratorsInviteHandler.inviteToProject(
|
|
|
|
|
- projectId,
|
|
|
|
|
- sendingUser,
|
|
|
|
|
- email,
|
|
|
|
|
- privileges,
|
|
|
|
|
- function (err, invite) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error creating project invite', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- email,
|
|
|
|
|
- sendingUserId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- logger.debug(
|
|
|
|
|
- { projectId, email, sendingUserId },
|
|
|
|
|
- 'invite created'
|
|
|
|
|
- )
|
|
|
|
|
- EditorRealTimeController.emitToRoom(
|
|
|
|
|
- projectId,
|
|
|
|
|
- 'project:membership:changed',
|
|
|
|
|
- { invites: true }
|
|
|
|
|
- )
|
|
|
|
|
- res.json({ invite })
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const allowed = await LimitationsManager.promises.canAddXCollaborators(
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ 1
|
|
|
|
|
+ )
|
|
|
|
|
+ if (!allowed) {
|
|
|
|
|
+ logger.debug(
|
|
|
|
|
+ { projectId, email, sendingUserId },
|
|
|
|
|
+ 'not allowed to invite more users to project'
|
|
|
)
|
|
)
|
|
|
- })
|
|
|
|
|
|
|
+ return res.json({ invite: null })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ email = EmailHelper.parseEmail(email, true)
|
|
|
|
|
+ if (email == null || email === '') {
|
|
|
|
|
+ logger.debug({ projectId, email, sendingUserId }, 'invalid email address')
|
|
|
|
|
+ return res.status(400).json({ errorReason: 'invalid_email' })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
|
|
|
|
|
+ sendingUserId
|
|
|
|
|
+ )
|
|
|
|
|
+ if (!underRateLimit) {
|
|
|
|
|
+ return res.sendStatus(429)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const shouldAllowInvite =
|
|
|
|
|
+ await CollaboratorsInviteController._checkShouldInviteEmail(email)
|
|
|
|
|
+ if (!shouldAllowInvite) {
|
|
|
|
|
+ logger.debug(
|
|
|
|
|
+ { email, projectId, sendingUserId },
|
|
|
|
|
+ 'not allowed to send an invite to this email address'
|
|
|
|
|
+ )
|
|
|
|
|
+ return res.json({
|
|
|
|
|
+ invite: null,
|
|
|
|
|
+ error: 'cannot_invite_non_user',
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const invite = await CollaboratorsInviteHandler.promises.inviteToProject(
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ sendingUser,
|
|
|
|
|
+ email,
|
|
|
|
|
+ privileges
|
|
|
|
|
+ )
|
|
|
|
|
+ logger.debug({ projectId, email, sendingUserId }, 'invite created')
|
|
|
|
|
+ EditorRealTimeController.emitToRoom(
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ 'project:membership:changed',
|
|
|
|
|
+ { invites: true }
|
|
|
|
|
+ )
|
|
|
|
|
+ res.json({ invite })
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- revokeInvite(req, res, next) {
|
|
|
|
|
|
|
+ async revokeInvite(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
const inviteId = req.params.invite_id
|
|
const inviteId = req.params.invite_id
|
|
|
|
|
+
|
|
|
logger.debug({ projectId, inviteId }, 'revoking invite')
|
|
logger.debug({ projectId, inviteId }, 'revoking invite')
|
|
|
- CollaboratorsInviteHandler.revokeInvite(
|
|
|
|
|
|
|
+ await CollaboratorsInviteHandler.promises.revokeInvite(projectId, inviteId)
|
|
|
|
|
+ EditorRealTimeController.emitToRoom(
|
|
|
projectId,
|
|
projectId,
|
|
|
- inviteId,
|
|
|
|
|
- function (err) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error revoking invite', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- inviteId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- EditorRealTimeController.emitToRoom(
|
|
|
|
|
- projectId,
|
|
|
|
|
- 'project:membership:changed',
|
|
|
|
|
- { invites: true }
|
|
|
|
|
- )
|
|
|
|
|
- res.sendStatus(201)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ 'project:membership:changed',
|
|
|
|
|
+ { invites: true }
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ res.sendStatus(201)
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- resendInvite(req, res, next) {
|
|
|
|
|
|
|
+ async resendInvite(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
const inviteId = req.params.invite_id
|
|
const inviteId = req.params.invite_id
|
|
|
|
|
+
|
|
|
logger.debug({ projectId, inviteId }, 'resending invite')
|
|
logger.debug({ projectId, inviteId }, 'resending invite')
|
|
|
const sendingUser = SessionManager.getSessionUser(req.session)
|
|
const sendingUser = SessionManager.getSessionUser(req.session)
|
|
|
- CollaboratorsInviteController._checkRateLimit(
|
|
|
|
|
- sendingUser._id,
|
|
|
|
|
- function (error, underRateLimit) {
|
|
|
|
|
- if (error) {
|
|
|
|
|
- return next(error)
|
|
|
|
|
- }
|
|
|
|
|
- if (!underRateLimit) {
|
|
|
|
|
- return res.sendStatus(429)
|
|
|
|
|
- }
|
|
|
|
|
- CollaboratorsInviteHandler.resendInvite(
|
|
|
|
|
- projectId,
|
|
|
|
|
- sendingUser,
|
|
|
|
|
- inviteId,
|
|
|
|
|
- function (err) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error resending invite', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- inviteId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- res.sendStatus(201)
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
|
|
|
|
|
+ sendingUser._id
|
|
|
|
|
+ )
|
|
|
|
|
+ if (!underRateLimit) {
|
|
|
|
|
+ return res.sendStatus(429)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await CollaboratorsInviteHandler.promises.resendInvite(
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ sendingUser,
|
|
|
|
|
+ inviteId
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ res.sendStatus(201)
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- viewInvite(req, res, next) {
|
|
|
|
|
|
|
+ async viewInvite(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
const { token } = req.params
|
|
const { token } = req.params
|
|
|
const _renderInvalidPage = function () {
|
|
const _renderInvalidPage = function () {
|
|
|
logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
|
|
logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
|
|
|
res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
|
res.render('project/invite/not-valid', { title: 'Invalid Invite' })
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
// check if the user is already a member of the project
|
|
// check if the user is already a member of the project
|
|
|
const currentUser = SessionManager.getSessionUser(req.session)
|
|
const currentUser = SessionManager.getSessionUser(req.session)
|
|
|
- CollaboratorsGetter.isUserInvitedMemberOfProject(
|
|
|
|
|
- currentUser._id,
|
|
|
|
|
|
|
+ const isMember =
|
|
|
|
|
+ await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
|
|
|
|
|
+ currentUser._id,
|
|
|
|
|
+ projectId
|
|
|
|
|
+ )
|
|
|
|
|
+ if (isMember) {
|
|
|
|
|
+ logger.debug(
|
|
|
|
|
+ { projectId, userId: currentUser._id },
|
|
|
|
|
+ 'user is already a member of this project, redirecting'
|
|
|
|
|
+ )
|
|
|
|
|
+ return res.redirect(`/project/${projectId}`)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // get the invite
|
|
|
|
|
+ const invite = await CollaboratorsInviteHandler.promises.getInviteByToken(
|
|
|
projectId,
|
|
projectId,
|
|
|
- function (err, isMember) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error checking if user is member of project', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- if (isMember) {
|
|
|
|
|
- logger.debug(
|
|
|
|
|
- { projectId, userId: currentUser._id },
|
|
|
|
|
- 'user is already a member of this project, redirecting'
|
|
|
|
|
- )
|
|
|
|
|
- return res.redirect(`/project/${projectId}`)
|
|
|
|
|
- }
|
|
|
|
|
- // get the invite
|
|
|
|
|
- CollaboratorsInviteHandler.getInviteByToken(
|
|
|
|
|
- projectId,
|
|
|
|
|
- token,
|
|
|
|
|
- function (err, invite) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error getting invite by token', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- // check if invite is gone, or otherwise non-existent
|
|
|
|
|
- if (invite == null) {
|
|
|
|
|
- logger.debug({ projectId }, 'no invite found for this token')
|
|
|
|
|
- return _renderInvalidPage()
|
|
|
|
|
- }
|
|
|
|
|
- // check the user who sent the invite exists
|
|
|
|
|
- UserGetter.getUser(
|
|
|
|
|
- { _id: invite.sendingUserId },
|
|
|
|
|
- { email: 1, first_name: 1, last_name: 1 },
|
|
|
|
|
- function (err, owner) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error getting project owner', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- if (owner == null) {
|
|
|
|
|
- logger.debug({ projectId }, 'no project owner found')
|
|
|
|
|
- return _renderInvalidPage()
|
|
|
|
|
- }
|
|
|
|
|
- // fetch the project name
|
|
|
|
|
- ProjectGetter.getProject(
|
|
|
|
|
- projectId,
|
|
|
|
|
- {},
|
|
|
|
|
- function (err, project) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error getting project', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- if (project == null) {
|
|
|
|
|
- logger.debug({ projectId }, 'no project found')
|
|
|
|
|
- return _renderInvalidPage()
|
|
|
|
|
- }
|
|
|
|
|
- // finally render the invite
|
|
|
|
|
- res.render('project/invite/show', {
|
|
|
|
|
- invite,
|
|
|
|
|
- project,
|
|
|
|
|
- owner,
|
|
|
|
|
- title: 'Project Invite',
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ token
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ // check if invite is gone, or otherwise non-existent
|
|
|
|
|
+ if (invite == null) {
|
|
|
|
|
+ logger.debug({ projectId }, 'no invite found for this token')
|
|
|
|
|
+ return _renderInvalidPage()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // check the user who sent the invite exists
|
|
|
|
|
+ const owner = await UserGetter.promises.getUser(
|
|
|
|
|
+ { _id: invite.sendingUserId },
|
|
|
|
|
+ { email: 1, first_name: 1, last_name: 1 }
|
|
|
|
|
+ )
|
|
|
|
|
+ if (owner == null) {
|
|
|
|
|
+ logger.debug({ projectId }, 'no project owner found')
|
|
|
|
|
+ return _renderInvalidPage()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // fetch the project name
|
|
|
|
|
+ const project = await ProjectGetter.promises.getProject(projectId, {
|
|
|
|
|
+ name: 1,
|
|
|
|
|
+ })
|
|
|
|
|
+ if (project == null) {
|
|
|
|
|
+ logger.debug({ projectId }, 'no project found')
|
|
|
|
|
+ return _renderInvalidPage()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // finally render the invite
|
|
|
|
|
+ res.render('project/invite/show', {
|
|
|
|
|
+ invite,
|
|
|
|
|
+ project,
|
|
|
|
|
+ owner,
|
|
|
|
|
+ title: 'Project Invite',
|
|
|
|
|
+ })
|
|
|
},
|
|
},
|
|
|
|
|
|
|
|
- acceptInvite(req, res, next) {
|
|
|
|
|
|
|
+ async acceptInvite(req, res) {
|
|
|
const projectId = req.params.Project_id
|
|
const projectId = req.params.Project_id
|
|
|
const { token } = req.params
|
|
const { token } = req.params
|
|
|
const currentUser = SessionManager.getSessionUser(req.session)
|
|
const currentUser = SessionManager.getSessionUser(req.session)
|
|
@@ -349,36 +255,44 @@ module.exports = CollaboratorsInviteController = {
|
|
|
{ projectId, userId: currentUser._id },
|
|
{ projectId, userId: currentUser._id },
|
|
|
'got request to accept invite'
|
|
'got request to accept invite'
|
|
|
)
|
|
)
|
|
|
- CollaboratorsInviteHandler.acceptInvite(
|
|
|
|
|
|
|
+
|
|
|
|
|
+ await CollaboratorsInviteHandler.promises.acceptInvite(
|
|
|
projectId,
|
|
projectId,
|
|
|
token,
|
|
token,
|
|
|
- currentUser,
|
|
|
|
|
- function (err) {
|
|
|
|
|
- if (err) {
|
|
|
|
|
- OError.tag(err, 'error accepting invite by token', {
|
|
|
|
|
- projectId,
|
|
|
|
|
- token,
|
|
|
|
|
- })
|
|
|
|
|
- return next(err)
|
|
|
|
|
- }
|
|
|
|
|
- EditorRealTimeController.emitToRoom(
|
|
|
|
|
- projectId,
|
|
|
|
|
- 'project:membership:changed',
|
|
|
|
|
- { invites: true, members: true }
|
|
|
|
|
- )
|
|
|
|
|
- AnalyticsManager.recordEventForUser(
|
|
|
|
|
- currentUser._id,
|
|
|
|
|
- 'project-invite-accept',
|
|
|
|
|
- {
|
|
|
|
|
- projectId,
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- if (req.xhr) {
|
|
|
|
|
- res.sendStatus(204) // Done async via project page notification
|
|
|
|
|
- } else {
|
|
|
|
|
- res.redirect(`/project/${projectId}`)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ currentUser
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ EditorRealTimeController.emitToRoom(
|
|
|
|
|
+ projectId,
|
|
|
|
|
+ 'project:membership:changed',
|
|
|
|
|
+ { invites: true, members: true }
|
|
|
|
|
+ )
|
|
|
|
|
+ AnalyticsManager.recordEventForUser(
|
|
|
|
|
+ currentUser._id,
|
|
|
|
|
+ 'project-invite-accept',
|
|
|
|
|
+ {
|
|
|
|
|
+ projectId,
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
|
|
+
|
|
|
|
|
+ if (req.xhr) {
|
|
|
|
|
+ res.sendStatus(204) // Done async via project page notification
|
|
|
|
|
+ } else {
|
|
|
|
|
+ res.redirect(`/project/${projectId}`)
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+module.exports = {
|
|
|
|
|
+ promises: CollaboratorsInviteController,
|
|
|
|
|
+ getAllInvites: expressify(CollaboratorsInviteController.getAllInvites),
|
|
|
|
|
+ inviteToProject: expressify(CollaboratorsInviteController.inviteToProject),
|
|
|
|
|
+ revokeInvite: expressify(CollaboratorsInviteController.revokeInvite),
|
|
|
|
|
+ resendInvite: expressify(CollaboratorsInviteController.resendInvite),
|
|
|
|
|
+ viewInvite: expressify(CollaboratorsInviteController.viewInvite),
|
|
|
|
|
+ acceptInvite: expressify(CollaboratorsInviteController.acceptInvite),
|
|
|
|
|
+ _checkShouldInviteEmail: callbackify(
|
|
|
|
|
+ CollaboratorsInviteController._checkShouldInviteEmail
|
|
|
|
|
+ ),
|
|
|
|
|
+ _checkRateLimit: callbackify(CollaboratorsInviteController._checkRateLimit),
|
|
|
|
|
+}
|