From 7559c05997b434514a8d68e6fa9bd3916f5a796b Mon Sep 17 00:00:00 2001 From: pierre-lehnen-rc <55164754+pierre-lehnen-rc@users.noreply.github.com> Date: Tue, 28 Jan 2020 15:02:26 -0300 Subject: [PATCH] [FIX] Invite links usage by channel owners/moderators (#16176) --- .../server/functions/findOrCreateInvite.js | 9 +++++---- app/invites/server/functions/useInviteToken.js | 16 +++++++++------- .../server/functions/validateInviteToken.js | 2 +- app/lib/server/functions/setUsername.js | 11 ++++++++++- app/models/server/models/Invites.js | 9 +++++++++ app/models/server/models/Users.js | 10 ++++++++++ app/ui-flextab/client/tabs/membersList.js | 2 +- app/utils/lib/getURL.js | 12 +++++++----- 8 files changed, 52 insertions(+), 19 deletions(-) diff --git a/app/invites/server/functions/findOrCreateInvite.js b/app/invites/server/functions/findOrCreateInvite.js index 6ba834a7481..cd01c0cc06e 100644 --- a/app/invites/server/functions/findOrCreateInvite.js +++ b/app/invites/server/functions/findOrCreateInvite.js @@ -15,6 +15,7 @@ function getInviteUrl(invite) { return getURL(`invite/${ _id }`, { full: useDirectLink, cloud: !useDirectLink, + cloud_route: 'invite', }); } @@ -26,14 +27,14 @@ export const findOrCreateInvite = (userId, invite) => { return false; } - if (!hasPermission(userId, 'create-invite-links')) { - throw new Meteor.Error('not_authorized'); - } - if (!invite.rid) { throw new Meteor.Error('error-the-field-is-required', 'The field rid is required', { method: 'findOrCreateInvite', field: 'rid' }); } + if (!hasPermission(userId, 'create-invite-links', invite.rid)) { + throw new Meteor.Error('not_authorized'); + } + const subscription = Subscriptions.findOneByRoomIdAndUserId(invite.rid, userId, { fields: { _id: 1 } }); if (!subscription) { throw new Meteor.Error('error-invalid-room', 'The rid field is invalid', { method: 'findOrCreateInvite', field: 'rid' }); diff --git a/app/invites/server/functions/useInviteToken.js b/app/invites/server/functions/useInviteToken.js index 51563956a05..90a7b3ec32d 100644 --- a/app/invites/server/functions/useInviteToken.js +++ b/app/invites/server/functions/useInviteToken.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Invites, Users } from '../../../models'; +import { Invites, Users } from '../../../models/server'; import { validateInviteToken } from './validateInviteToken'; import { addUserToRoom } from '../../../lib/server/functions/addUserToRoom'; @@ -16,18 +16,20 @@ export const useInviteToken = (userId, token) => { const { inviteData, room } = validateInviteToken(token); const user = Users.findOneById(userId); + Users.updateInviteToken(user._id, token); - if (addUserToRoom(room._id, user)) { - Invites.update(inviteData._id, { - $inc: { - uses: 1, - }, - }); + Invites.increaseUsageById(inviteData._id); + + // If the user already has an username, then join the invite room, + // If no username is set yet, then the the join will happen on the setUsername method + if (user.username) { + addUserToRoom(room._id, user); } return { room: { rid: inviteData.rid, + prid: room.prid, fname: room.fname, name: room.name, t: room.t, diff --git a/app/invites/server/functions/validateInviteToken.js b/app/invites/server/functions/validateInviteToken.js index 528b268d51a..88d35fd5cca 100644 --- a/app/invites/server/functions/validateInviteToken.js +++ b/app/invites/server/functions/validateInviteToken.js @@ -12,7 +12,7 @@ export const validateInviteToken = (token) => { throw new Meteor.Error('error-invalid-token', 'The invite token is invalid.', { method: 'validateInviteToken', field: 'token' }); } - const room = Rooms.findOneById(inviteData.rid, { fields: { _id: 1, name: 1, fname: 1, t: 1 } }); + const room = Rooms.findOneById(inviteData.rid, { fields: { _id: 1, name: 1, fname: 1, t: 1, prid: 1 } }); if (!room) { throw new Meteor.Error('error-invalid-room', 'The invite token is invalid.', { method: 'validateInviteToken', field: 'rid' }); } diff --git a/app/lib/server/functions/setUsername.js b/app/lib/server/functions/setUsername.js index baed79351cf..d705d436c08 100644 --- a/app/lib/server/functions/setUsername.js +++ b/app/lib/server/functions/setUsername.js @@ -4,10 +4,11 @@ import { Accounts } from 'meteor/accounts-base'; import { FileUpload } from '../../../file-upload'; import { settings } from '../../../settings'; -import { Users, Messages, Subscriptions, Rooms, LivechatDepartmentAgents } from '../../../models'; +import { Users, Messages, Subscriptions, Rooms, LivechatDepartmentAgents, Invites } from '../../../models'; import { hasPermission } from '../../../authorization'; import { RateLimiter } from '../lib'; import { Notifications } from '../../../notifications/server'; +import { addUserToRoom } from './addUserToRoom'; import { checkUsernameAvailability, setUserAvatar, getAvatarSuggestionForUser } from '.'; @@ -87,6 +88,14 @@ export const _setUsername = function(userId, u) { } } + // If it's the first username and the user has an invite Token, then join the invite room + if (!previousUsername && user.inviteToken) { + const inviteData = Invites.findOneById(user.inviteToken); + if (inviteData && inviteData.rid) { + addUserToRoom(inviteData.rid, user); + } + } + Notifications.notifyLogged('Users:NameChanged', { _id: user._id, name: user.name, diff --git a/app/models/server/models/Invites.js b/app/models/server/models/Invites.js index c429de2cbe6..517c1780f0b 100644 --- a/app/models/server/models/Invites.js +++ b/app/models/server/models/Invites.js @@ -37,6 +37,15 @@ class Invites extends Base { removeById(_id) { return this.remove({ _id }); } + + // UPDATE + increaseUsageById(_id, uses = 1) { + return this.update({ _id }, { + $inc: { + uses, + }, + }); + } } export default new Invites(); diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index d4dd87f594f..dac4c1f6522 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -803,6 +803,16 @@ export class Users extends Base { return this.update(query, update); } + updateInviteToken(_id, inviteToken) { + const update = { + $set: { + inviteToken, + }, + }; + + return this.update(_id, update); + } + updateStatusText(_id, statusText) { const update = { $set: { diff --git a/app/ui-flextab/client/tabs/membersList.js b/app/ui-flextab/client/tabs/membersList.js index 5f4ec5cb8a2..c7a6a3ec86b 100644 --- a/app/ui-flextab/client/tabs/membersList.js +++ b/app/ui-flextab/client/tabs/membersList.js @@ -102,7 +102,7 @@ Template.membersList.helpers({ }, canInviteUser() { - return hasPermission('create-invite-links'); + return hasPermission('create-invite-links', this._id); }, showUserInfo() { diff --git a/app/utils/lib/getURL.js b/app/utils/lib/getURL.js index 9d8b07a4b00..7b52a1ba842 100644 --- a/app/utils/lib/getURL.js +++ b/app/utils/lib/getURL.js @@ -3,13 +3,13 @@ import s from 'underscore.string'; import { isURL } from './isURL'; import { settings } from '../../settings'; -function getCloudUrl(path, _site_url) { +function getCloudUrl(path, _site_url, cloudRoute) { const siteUrl = s.rtrim(_site_url, '/'); // Remove the protocol const host = siteUrl.replace(/https?\:\/\//i, ''); path = s.ltrim(path, '/'); - const url = `https://go.rocket.chat/?host=${ encodeURIComponent(host) }&path=${ encodeURIComponent(path) }`; + const url = `https://go.rocket.chat/${ cloudRoute }?host=${ encodeURIComponent(host) }&path=${ encodeURIComponent(path) }`; if (siteUrl.includes('http://')) { return `${ url }&secure=no`; @@ -18,7 +18,7 @@ function getCloudUrl(path, _site_url) { return url; } -export const _getURL = (path, { cdn, full, cloud, _cdn_prefix, _root_url_path_prefix, _site_url }) => { +export const _getURL = (path, { cdn, full, cloud, cloud_route, _cdn_prefix, _root_url_path_prefix, _site_url }) => { if (isURL(path)) { return path; } @@ -28,6 +28,7 @@ export const _getURL = (path, { cdn, full, cloud, _cdn_prefix, _root_url_path_pr const query = _query ? `?${ _query }` : ''; const siteUrl = s.rtrim(s.trim(_site_url || ''), '/'); + const cloudRoute = s.trim(cloud_route || ''); const cdnPrefix = s.rtrim(s.trim(_cdn_prefix || ''), '/'); const pathPrefix = s.rtrim(s.trim(_root_url_path_prefix || ''), '/'); @@ -44,16 +45,17 @@ export const _getURL = (path, { cdn, full, cloud, _cdn_prefix, _root_url_path_pr } if (cloud) { - return getCloudUrl(url, siteUrl); + return getCloudUrl(url, siteUrl, cloudRoute); } return url; }; -export const getURL = (path, { cdn = true, full = false, cloud = false } = {}) => _getURL(path, { +export const getURL = (path, { cdn = true, full = false, cloud = false, cloud_route = '' } = {}) => _getURL(path, { cdn, full, cloud, + cloud_route, _cdn_prefix: settings.get('CDN_PREFIX'), _root_url_path_prefix: __meteor_runtime_config__.ROOT_URL_PATH_PREFIX, _site_url: settings.get('Site_Url'),