Bump version to 4.1.1

pull/23645/head
Diego Sampaio 4 years ago
parent 1131e0014c
commit 672fe95d7e
No known key found for this signature in database
GPG Key ID: E060152B30502562
  1. 2
      .docker/Dockerfile.rhel
  2. 7
      .github/history-manual.json
  3. 45
      .github/history.json
  4. 2
      .snapcraft/resources/prepareRocketChat
  5. 2
      .snapcraft/snap/snapcraft.yaml
  6. 2123
      HISTORY.md
  7. 6
      app/api/server/v1/channels.js
  8. 8
      app/api/server/v1/chat.js
  9. 20
      app/api/server/v1/commands.js
  10. 4
      app/api/server/v1/im.js
  11. 12
      app/api/server/v1/rooms.js
  12. 13
      app/e2e/server/methods/getUsersOfRoomWithoutKey.js
  13. 16
      app/e2e/server/methods/setRoomKeyID.js
  14. 24
      app/lib/server/lib/processDirectEmail.js
  15. 10
      app/lib/server/methods/getChannelHistory.js
  16. 24
      app/lib/server/methods/getMessages.js
  17. 5
      app/lib/server/methods/getSingleMessage.js
  18. 2
      app/livechat/client/views/app/livechatReadOnly.js
  19. 21
      app/message-pin/server/pinMessage.js
  20. 14
      app/message-star/server/starMessage.js
  21. 11
      app/models/server/models/Messages.js
  22. 18
      app/reactions/server/setReaction.js
  23. 44
      app/search/server/service/validationService.js
  24. 4
      app/threads/server/methods/followMessage.js
  25. 8
      app/threads/server/methods/unfollowMessage.js
  26. 2
      app/utils/rocketchat.info
  27. 31
      app/videobridge/server/methods/bbb.js
  28. 6
      imports/message-read-receipt/server/api/methods/getReadReceipts.js
  29. 2
      package-lock.json
  30. 2
      package.json
  31. 85
      server/methods/canAccessRoom.js
  32. 16
      server/methods/getUsersOfRoom.js
  33. 16
      server/methods/loadHistory.js
  34. 12
      server/methods/loadMissedMessages.js
  35. 11
      server/methods/loadNextMessages.js
  36. 7
      server/methods/loadSurroundingMessages.js
  37. 7
      server/methods/messageSearch.js
  38. 9
      server/publications/messages.js
  39. 10
      server/publications/room/index.js
  40. 31
      tests/end-to-end/api/05-chat.js
  41. 3
      tests/end-to-end/api/24-methods.js

@ -1,6 +1,6 @@
FROM registry.access.redhat.com/ubi8/nodejs-12
ENV RC_VERSION 4.1.0
ENV RC_VERSION 4.1.1
MAINTAINER buildmaster@rocket.chat

@ -123,5 +123,12 @@
"sampaiodiego",
"pierre-lehnen-rc"
]
}],
"4.1.1": [{
"title": "[FIX] Security Hotfix (https://docs.rocket.chat/guides/security/security-updates)",
"userLogin": "sampaiodiego",
"contributors": [
"sampaiodiego"
]
}]
}

@ -67032,6 +67032,49 @@
"5.0"
],
"pull_requests": []
},
"4.1.1": {
"node_version": "12.22.1",
"npm_version": "6.14.1",
"apps_engine_version": "1.28.1",
"mongo_versions": [
"3.6",
"4.0",
"4.2",
"4.4",
"5.0"
],
"pull_requests": [
{
"pr": "23607",
"title": "[FIX] App update flow failing in HA setups",
"userLogin": "d-gubert",
"description": "The flow for app updates is broken in specific scenarios with HA setups. Here we change the method calls in the Apps-Engine to avoid race conditions",
"milestone": "4.1.1",
"contributors": [
"d-gubert"
]
},
{
"pr": "23627",
"title": "[FIX] LDAP users not being re-activated on login",
"userLogin": "pierre-lehnen-rc",
"milestone": "4.1.1",
"contributors": [
"pierre-lehnen-rc"
]
},
{
"pr": "23608",
"title": "[FIX] Advanced LDAP Sync Features",
"userLogin": "pierre-lehnen-rc",
"milestone": "4.1.1",
"contributors": [
"pierre-lehnen-rc",
"web-flow"
]
}
]
}
}
}
}

@ -1,6 +1,6 @@
#!/bin/bash
curl -SLf "https://releases.rocket.chat/4.1.0/download/" -o rocket.chat.tgz
curl -SLf "https://releases.rocket.chat/4.1.1/download/" -o rocket.chat.tgz
tar xf rocket.chat.tgz --strip 1

@ -7,7 +7,7 @@
# 5. `snapcraft snap`
name: rocketchat-server
version: 4.1.0
version: 4.1.1
summary: Rocket.Chat server
description: Have your own Slack like online chat, built with Meteor. https://rocket.chat/
confinement: strict

File diff suppressed because it is too large Load Diff

@ -285,9 +285,9 @@ API.v1.addRoute('channels.files', { authRequired: true }, {
return file;
};
Meteor.runAsUser(this.userId, () => {
Meteor.call('canAccessRoom', findResult._id, this.userId);
});
if (!canAccessRoom(findResult, { _id: this.userId })) {
return API.v1.unauthorized();
}
const { offset, count } = this.getPaginationItems();
const { sort, fields, query } = this.parseJsonQuery();

@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Messages } from '../../../models';
import { canAccessRoom, hasPermission } from '../../../authorization';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { processWebhookMessage } from '../../../lib/server';
import { executeSendMessage } from '../../../lib/server/methods/sendMessage';
@ -404,12 +404,12 @@ API.v1.addRoute('chat.getPinnedMessages', { authRequired: true }, {
if (!roomId) {
throw new Meteor.Error('error-roomId-param-not-provided', 'The required "roomId" query param is missing.');
}
const room = Meteor.call('canAccessRoom', roomId, this.userId);
if (!room) {
if (!canAccessRoom({ _id: roomId }, { _id: this.userId })) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
const cursor = Messages.findPinnedByRoom(room._id, {
const cursor = Messages.findPinnedByRoom(roomId, {
skip: offset,
limit: count,
});

@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import objectPath from 'object-path';
import { slashCommands } from '../../../utils';
import { Messages } from '../../../models';
import { slashCommands } from '../../../utils/server';
import { Messages } from '../../../models/server';
import { canAccessRoom } from '../../../authorization/server';
import { API } from '../api';
API.v1.addRoute('commands.get', { authRequired: true }, {
@ -189,8 +190,9 @@ API.v1.addRoute('commands.run', { authRequired: true }, {
return API.v1.failure('The command provided does not exist (or is disabled).');
}
// This will throw an error if they can't or the room is invalid
Meteor.call('canAccessRoom', body.roomId, user._id);
if (!canAccessRoom({ _id: body.roomId }, user)) {
return API.v1.unauthorized();
}
const params = body.params ? body.params : '';
const message = {
@ -238,8 +240,9 @@ API.v1.addRoute('commands.preview', { authRequired: true }, {
return API.v1.failure('The command provided does not exist (or is disabled).');
}
// This will throw an error if they can't or the room is invalid
Meteor.call('canAccessRoom', query.roomId, user._id);
if (!canAccessRoom({ _id: query.roomId }, user)) {
return API.v1.unauthorized();
}
const params = query.params ? query.params : '';
@ -288,8 +291,9 @@ API.v1.addRoute('commands.preview', { authRequired: true }, {
return API.v1.failure('The command provided does not exist (or is disabled).');
}
// This will throw an error if they can't or the room is invalid
Meteor.call('canAccessRoom', body.roomId, user._id);
if (!canAccessRoom({ _id: body.roomId }, user)) {
return API.v1.unauthorized();
}
const params = body.params ? body.params : '';
const message = {

@ -2,7 +2,7 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Subscriptions, Uploads, Users, Messages, Rooms } from '../../../models/server';
import { hasPermission } from '../../../authorization/server';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { settings } from '../../../settings/server';
import { API } from '../api';
@ -19,7 +19,7 @@ function findDirectMessageRoom(params, user, allowAdminOverride) {
nameOrId: params.username || params.roomId,
});
const canAccess = Meteor.call('canAccessRoom', room._id, user._id)
const canAccess = canAccessRoom(room, user)
|| (allowAdminOverride && hasPermission(user._id, 'view-room-administration'));
if (!canAccess || !room || room.t !== 'd') {
throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "username" param provided does not match any direct message');

@ -65,9 +65,7 @@ API.v1.addRoute('rooms.get', { authRequired: true }, {
API.v1.addRoute('rooms.upload/:rid', { authRequired: true }, {
post() {
const room = Meteor.call('canAccessRoom', this.urlParams.rid, this.userId);
if (!room) {
if (!canAccessRoom({ _id: this.urlParams.rid }, { _id: this.userId })) {
return API.v1.unauthorized();
}
@ -191,9 +189,11 @@ API.v1.addRoute('rooms.info', { authRequired: true }, {
get() {
const room = findRoomByIdOrName({ params: this.requestParams() });
const { fields } = this.parseJsonQuery();
if (!Meteor.call('canAccessRoom', room._id, this.userId, {})) {
if (!room || !canAccessRoom(room, { _id: this.userId })) {
return API.v1.failure('not-allowed', 'Not Allowed');
}
return API.v1.success({ room: Rooms.findOneByIdOrName(room._id, { fields }) });
},
});
@ -244,9 +244,11 @@ API.v1.addRoute('rooms.getDiscussions', { authRequired: true }, {
const room = findRoomByIdOrName({ params: this.requestParams() });
const { offset, count } = this.getPaginationItems();
const { sort, fields, query } = this.parseJsonQuery();
if (!Meteor.call('canAccessRoom', room._id, this.userId, {})) {
if (!room || !canAccessRoom(room, { _id: this.userId })) {
return API.v1.failure('not-allowed', 'Not Allowed');
}
const ourQuery = Object.assign(query, { prid: room._id });
const discussions = Rooms.find(ourQuery, {

@ -1,16 +1,23 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Subscriptions, Users } from '../../../models';
import { canAccessRoom } from '../../../authorization/server';
import { Subscriptions, Users } from '../../../models/server';
Meteor.methods({
'e2e.getUsersOfRoomWithoutKey'(rid) {
check(rid, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.getUsersOfRoomWithoutKey' });
}
const room = Meteor.call('canAccessRoom', rid, userId);
if (!room) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' });
}
if (!canAccessRoom({ _id: rid }, { _id: userId })) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' });
}

@ -1,19 +1,29 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Rooms } from '../../../models';
import { canAccessRoom } from '../../../authorization/server';
import { Rooms } from '../../../models/server';
Meteor.methods({
'e2e.setRoomKeyID'(rid, keyID) {
check(rid, String);
check(keyID, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.setRoomKeyID' });
}
const room = Meteor.call('canAccessRoom', rid, userId);
if (!room) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
}
if (!canAccessRoom({ _id: rid }, { _id: userId })) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
}
const room = Rooms.findOneById(rid, { fields: { e2eKeyId: 1 } });
if (room.e2eKeyId) {
throw new Meteor.Error('error-room-e2e-key-already-exists', 'E2E Key ID already exists', { method: 'e2e.setRoomKeyID' });
}

@ -5,7 +5,7 @@ import moment from 'moment';
import { settings } from '../../../settings/server';
import { Rooms, Messages, Users, Subscriptions } from '../../../models/server';
import { metrics } from '../../../metrics/server';
import { hasPermission } from '../../../authorization/server';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { SystemLogger } from '../../../../server/lib/logger/system';
import { sendMessage as _sendMessage } from '../functions';
@ -55,29 +55,25 @@ export const processDirectEmail = function(email) {
}
message.rid = prevMessage.rid;
const room = Meteor.call('canAccessRoom', message.rid, user._id);
if (!room) {
const room = Rooms.findOneById(message.rid);
if (!canAccessRoom(room, user)) {
return false;
}
const roomInfo = Rooms.findOneById(message.rid, {
t: 1,
name: 1,
});
// check mention
if (message.msg.indexOf(`@${ prevMessage.u.username }`) === -1 && roomInfo.t !== 'd') {
if (message.msg.indexOf(`@${ prevMessage.u.username }`) === -1 && room.t !== 'd') {
message.msg = `@${ prevMessage.u.username } ${ message.msg }`;
}
// reply message link
let prevMessageLink = `[ ](${ Meteor.absoluteUrl().replace(/\/$/, '') }`;
if (roomInfo.t === 'c') {
prevMessageLink += `/channel/${ roomInfo.name }?msg=${ email.headers.mid }) `;
} else if (roomInfo.t === 'd') {
if (room.t === 'c') {
prevMessageLink += `/channel/${ room.name }?msg=${ email.headers.mid }) `;
} else if (room.t === 'd') {
prevMessageLink += `/direct/${ prevMessage.u.username }?msg=${ email.headers.mid }) `;
} else if (roomInfo.t === 'p') {
prevMessageLink += `/group/${ roomInfo.name }?msg=${ email.headers.mid }) `;
} else if (room.t === 'p') {
prevMessageLink += `/group/${ room.name }?msg=${ email.headers.mid }) `;
}
// add reply message link
message.msg = prevMessageLink + message.msg;

@ -2,8 +2,8 @@ import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import _ from 'underscore';
import { hasPermission } from '../../../authorization/server';
import { Subscriptions, Messages } from '../../../models/server';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { Subscriptions, Messages, Rooms } from '../../../models/server';
import { settings } from '../../../settings/server';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { getHiddenSystemMessages } from '../lib/getHiddenSystemMessages';
@ -17,11 +17,15 @@ Meteor.methods({
}
const fromUserId = Meteor.userId();
const room = Meteor.call('canAccessRoom', rid, fromUserId);
const room = Rooms.findOneById(rid);
if (!room) {
return false;
}
if (!canAccessRoom(room, { _id: fromUserId })) {
return false;
}
// Make sure they can access the room
if (room.t === 'c' && !hasPermission(fromUserId, 'preview-c-room') && !Subscriptions.findOneByRoomIdAndUserId(rid, fromUserId, { fields: { _id: 1 } })) {
return false;

@ -1,28 +1,22 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../../models';
import { canAccessRoom } from '../../../authorization/server';
import { Messages } from '../../../models/server';
Meteor.methods({
getMessages(messages) {
check(messages, [String]);
const cache = {};
const msgs = Messages.findVisibleByIds(messages).fetch();
return messages.map((msgId) => {
const msg = Messages.findOneById(msgId);
const user = { _id: Meteor.userId() };
if (!msg || !msg.rid) {
return undefined;
}
const rids = [...new Set(msgs.map((m) => m.rid))];
if (!rids.every((_id) => canAccessRoom({ _id }, user))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' });
}
cache[msg.rid] = cache[msg.rid] || Meteor.call('canAccessRoom', msg.rid, Meteor.userId());
if (!cache[msg.rid]) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' });
}
return msg;
});
return msgs;
},
});

@ -1,7 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../../models';
import { canAccessRoom } from '../../../authorization/server';
import { Messages } from '../../../models/server';
Meteor.methods({
getSingleMessage(msgId) {
@ -13,7 +14,7 @@ Meteor.methods({
return undefined;
}
if (!Meteor.call('canAccessRoom', msg.rid, Meteor.userId())) {
if (!canAccessRoom({ _id: msg.rid }, { _id: Meteor.userId() })) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' });
}

@ -64,7 +64,7 @@ Template.livechatReadOnly.onCreated(function() {
this.preparing = new ReactiveVar(true);
this.updateInquiry = async ({ clientAction, ...inquiry }) => {
if (clientAction === 'removed' || !await callWithErrorHandling('canAccessRoom', inquiry.rid, Meteor.userId())) {
if (clientAction === 'removed') {
// this will force to refresh the room
// since the client wont get notified of room changes when chats are on queue (no one assigned)
// a better approach should be performed when refactoring these templates to use react

@ -1,11 +1,11 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { settings } from '../../settings';
import { callbacks } from '../../callbacks';
import { isTheLastMessage } from '../../lib';
import { settings } from '../../settings/server';
import { callbacks } from '../../callbacks/server';
import { isTheLastMessage } from '../../lib/server';
import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL';
import { hasPermission } from '../../authorization';
import { canAccessRoom, hasPermission } from '../../authorization/server';
import { Subscriptions, Messages, Users, Rooms } from '../../models';
const recursiveRemove = (msg, deep = 1) => {
@ -72,7 +72,11 @@ Meteor.methods({
if (settings.get('Message_KeepHistory')) {
Messages.cloneAndSaveAsHistoryById(message._id, me);
}
const room = Meteor.call('canAccessRoom', originalMessage.rid, Meteor.userId());
const room = Rooms.findOneById(originalMessage.rid);
if (!canAccessRoom(room, { _id: Meteor.userId() })) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'pinMessage' });
}
originalMessage.pinned = true;
originalMessage.pinnedAt = pinnedAt || Date.now;
@ -166,7 +170,12 @@ Meteor.methods({
username: me.username,
};
originalMessage = callbacks.run('beforeSaveMessage', originalMessage);
const room = Meteor.call('canAccessRoom', originalMessage.rid, Meteor.userId());
const room = Rooms.findOneById(originalMessage.rid, { fields: { lastMessage: 1 } });
if (!canAccessRoom(room, { _id: Meteor.userId() })) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' });
}
if (isTheLastMessage(room, message)) {
Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned);
}

@ -1,8 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { settings } from '../../settings';
import { isTheLastMessage } from '../../lib';
import { Subscriptions, Rooms, Messages } from '../../models';
import { settings } from '../../settings/server';
import { isTheLastMessage } from '../../lib/server';
import { canAccessRoom } from '../../authorization/server';
import { Subscriptions, Rooms, Messages } from '../../models/server';
Meteor.methods({
starMessage(message) {
@ -26,7 +27,12 @@ Meteor.methods({
if (!Messages.findOneByRoomIdAndMessageId(message.rid, message._id)) {
return false;
}
const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
const room = Rooms.findOneById(message.rid, { fields: { lastMessage: 1 } });
if (!canAccessRoom(room, { _id: Meteor.userId() })) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'starMessage' });
}
if (isTheLastMessage(room, message)) {
Rooms.updateLastMessageStar(room._id, Meteor.userId(), message.starred);
}

@ -246,6 +246,17 @@ export class Messages extends Base {
return this.find(query, options);
}
findVisibleByIds(ids, options) {
const query = {
_id: { $in: ids },
_hidden: {
$ne: true,
},
};
return this.find(query, options);
}
findVisibleThreadByThreadId(tmid, options) {
const query = {
_hidden: {

@ -2,11 +2,11 @@ import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import _ from 'underscore';
import { Messages, EmojiCustom, Rooms } from '../../models';
import { callbacks } from '../../callbacks';
import { emoji } from '../../emoji';
import { isTheLastMessage, msgStream } from '../../lib';
import { hasPermission } from '../../authorization/server/functions/hasPermission';
import { Messages, EmojiCustom, Rooms } from '../../models/server';
import { callbacks } from '../../callbacks/server';
import { emoji } from '../../emoji/server';
import { isTheLastMessage, msgStream } from '../../lib/server';
import { canAccessRoom, hasPermission } from '../../authorization/server';
import { api } from '../../../server/sdk/api';
const removeUserReaction = (message, reaction, username) => {
@ -91,17 +91,19 @@ export const executeSetReaction = async function(reaction, messageId, shouldReac
}
const message = Messages.findOneById(messageId);
if (!message) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}
const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
const room = Rooms.findOneById(message.rid);
if (!room) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'setReaction' });
}
return setReaction(room, user, message, reaction, shouldReact);
};

@ -1,42 +1,46 @@
import { Meteor } from 'meteor/meteor';
import mem from 'mem';
import SearchLogger from '../logger/logger';
import { Users } from '../../../models';
import { canAccessRoom } from '../../../authorization/server';
import { Users, Rooms } from '../../../models/server';
class ValidationService {
validateSearchResult(result) {
const subscriptionCache = {};
const getSubscription = mem((rid, uid) => {
if (!rid) {
return;
}
const getSubscription = (rid, uid) => {
if (!subscriptionCache.hasOwnProperty(rid)) {
subscriptionCache[rid] = Meteor.call('canAccessRoom', rid, uid);
const room = Rooms.findOneById(rid);
if (!room) {
return;
}
return subscriptionCache[rid];
};
if (!canAccessRoom(room, { _id: uid })) {
return;
}
const userCache = {};
return room;
});
const getUsername = (uid) => {
if (!userCache.hasOwnProperty(uid)) {
try {
userCache[uid] = Users.findById(uid).fetch()[0].username;
} catch (e) {
userCache[uid] = undefined;
}
const getUser = mem((uid) => {
if (!uid) {
return;
}
return userCache[uid];
};
return Users.findOneById(uid, { fields: { username: 1 } });
});
const uid = Meteor.userId();
// get subscription for message
if (result.message) {
result.message.docs.forEach((msg) => {
const user = getUser(msg.user);
const subscription = getSubscription(msg.rid, uid);
if (subscription) {
msg.r = { name: subscription.name, t: subscription.t };
msg.username = getUsername(msg.user);
msg.username = user?.username;
msg.valid = true;
SearchLogger.debug(`user ${ uid } can access ${ msg.rid } ( ${ subscription.t === 'd' ? subscription.username : subscription.name } )`);
} else {
@ -44,7 +48,7 @@ class ValidationService {
}
});
result.message.docs.filter((msg) => msg.valid);
result.message.docs = result.message.docs.filter((msg) => msg.valid);
}
if (result.room) {
@ -60,7 +64,7 @@ class ValidationService {
}
});
result.room.docs.filter((room) => room.valid);
result.room.docs = result.room.docs.filter((room) => room.valid);
}
return result;

@ -4,6 +4,7 @@ import { check } from 'meteor/check';
import { Messages } from '../../../models/server';
import { RateLimiter } from '../../../lib/server';
import { settings } from '../../../settings/server';
import { canAccessRoom } from '../../../authorization/server';
import { follow } from '../functions';
Meteor.methods({
@ -24,8 +25,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-message', 'Invalid message', { method: 'followMessage' });
}
const room = Meteor.call('canAccessRoom', message.rid, uid);
if (!room) {
if (!canAccessRoom({ _id: message.rid }, { _id: uid })) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });
}

@ -4,6 +4,7 @@ import { check } from 'meteor/check';
import { Messages } from '../../../models/server';
import { RateLimiter } from '../../../lib/server';
import { settings } from '../../../settings/server';
import { canAccessRoom } from '../../../authorization/server';
import { unfollow } from '../functions';
Meteor.methods({
@ -21,12 +22,11 @@ Meteor.methods({
const message = Messages.findOneById(mid, { fields: { rid: 1, tmid: 1 } });
if (!message) {
throw new Meteor.Error('error-invalid-message', 'Invalid message', { method: 'followMessage' });
throw new Meteor.Error('error-invalid-message', 'Invalid message', { method: 'unfollowMessage' });
}
const room = Meteor.call('canAccessRoom', message.rid, uid);
if (!room) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });
if (!canAccessRoom({ _id: message.rid }, { _id: uid })) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
}
return unfollow({ rid: message.rid, tmid: message.tmid || message._id, uid });

@ -1,3 +1,3 @@
{
"version": "4.1.0"
"version": "4.1.1"
}

@ -1,5 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import { check } from 'meteor/check';
import xml2js from 'xml2js';
import BigBlueButtonApi from '../../../bigbluebutton/server';
@ -7,6 +8,7 @@ import { SystemLogger } from '../../../../server/lib/logger/system';
import { settings } from '../../../settings/server';
import { Rooms, Users } from '../../../models/server';
import { saveStreamingOptions } from '../../../channel-settings/server';
import { canAccessRoom } from '../../../authorization/server';
import { API } from '../../../api/server';
const parser = new xml2js.Parser({
@ -24,11 +26,27 @@ const getBBBAPI = () => {
Meteor.methods({
bbbJoin({ rid }) {
check(rid, String);
if (!this.userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbJoin' });
}
if (!Meteor.call('canAccessRoom', rid, this.userId)) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'bbbJoin' });
}
const user = Users.findOneById(this.userId);
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbJoin' });
}
const room = Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'bbbJoin' });
}
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbJoin' });
}
@ -38,7 +56,6 @@ Meteor.methods({
const { api } = getBBBAPI();
const meetingID = settings.get('uniqueID') + rid;
const room = Rooms.findOneById(rid);
const createUrl = api.urlFor('create', {
name: room.t === 'd' ? 'Direct' : room.name,
meetingID,
@ -56,8 +73,6 @@ Meteor.methods({
const doc = parseString(createResult.content);
if (doc.response.returncode[0]) {
const user = Users.findOneById(this.userId);
const hookApi = api.urlFor('hooks/create', {
meetingID,
callbackURL: Meteor.absoluteUrl(`api/v1/videoconference.bbb.update/${ meetingID }`),
@ -90,11 +105,17 @@ Meteor.methods({
},
bbbEnd({ rid }) {
check(rid, String);
if (!this.userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbEnd' });
}
if (!Meteor.call('canAccessRoom', rid, this.userId)) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'bbbEnd' });
}
if (!canAccessRoom({ _id: rid }, { _id: this.userId })) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbEnd' });
}

@ -1,6 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Messages } from '../../../../../app/models';
import { Messages } from '../../../../../app/models/server';
import { canAccessRoom } from '../../../../../app/authorization/server';
import { ReadReceipt } from '../../lib/ReadReceipt';
Meteor.methods({
@ -19,8 +20,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-message', 'Invalid message', { method: 'getReadReceipts' });
}
const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
if (!room) {
if (!canAccessRoom({ _id: message.rid }, { _id: Meteor.userId() })) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getReadReceipts' });
}

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "Rocket.Chat",
"version": "4.1.0",
"version": "4.1.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,7 +1,7 @@
{
"name": "Rocket.Chat",
"description": "The Ultimate Open Source WebChat Platform",
"version": "4.1.0",
"version": "4.1.1",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"

@ -5,54 +5,57 @@ import { Users, Rooms } from '../../app/models/server';
import { canAccessRoom } from '../../app/authorization/server';
import { settings } from '../../app/settings/server';
Meteor.methods({
canAccessRoom(rid, userId, extraData) {
check(rid, String);
check(userId, Match.Maybe(String));
let user;
if (['yes', 'true'].includes(String(process.env.ALLOW_CANACCESSROOM_METHOD).toLowerCase())) {
console.warn('Method canAccessRoom is deprecated and will be removed after version 5.0');
Meteor.methods({
canAccessRoom(rid, userId, extraData) {
check(rid, String);
check(userId, Match.Maybe(String));
let user;
if (userId) {
user = Users.findOneById(userId, {
fields: {
username: 1,
},
});
if (userId) {
user = Users.findOneById(userId, {
fields: {
username: 1,
},
});
if (!user || !user.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'canAccessRoom',
});
}
}
if (!user || !user.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'canAccessRoom',
});
}
}
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'canAccessRoom',
});
}
const room = Rooms.findOneById(rid);
const room = Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'canAccessRoom',
});
}
if (!room) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'canAccessRoom',
});
}
if (canAccessRoom.call(this, room, user, extraData)) {
if (user) {
room.username = user.username;
}
return room;
}
if (canAccessRoom.call(this, room, user, extraData)) {
if (user) {
room.username = user.username;
if (!userId && settings.get('Accounts_AllowAnonymousRead') === false) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'canAccessRoom',
});
}
return room;
}
if (!userId && settings.get('Accounts_AllowAnonymousRead') === false) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'canAccessRoom',
});
}
return false;
},
});
return false;
},
});
}

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Subscriptions } from '../../app/models/server';
import { hasPermission } from '../../app/authorization/server';
import { Subscriptions, Rooms } from '../../app/models/server';
import { canAccessRoom, hasPermission } from '../../app/authorization/server';
import { findUsersOfRoom } from '../lib/findUsersOfRoom';
Meteor.methods({
@ -11,11 +11,19 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getUsersOfRoom' });
}
const room = Meteor.call('canAccessRoom', rid, userId);
if (!room) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' });
}
const room = Rooms.findOneById(rid, { fields: { broadcast: 1 } });
if (!room) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getUsersOfRoom' });
}
if (!canAccessRoom(room, { _id: userId })) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'getUsersOfRoom' });
}
if (room.broadcast && !hasPermission(userId, 'view-broadcast-member-list', rid)) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getUsersOfRoom' });
}

@ -1,9 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Subscriptions } from '../../app/models';
import { hasPermission } from '../../app/authorization';
import { settings } from '../../app/settings';
import { Subscriptions, Rooms } from '../../app/models/server';
import { canAccessRoom, hasPermission } from '../../app/authorization/server';
import { settings } from '../../app/settings/server';
import { loadMessageHistory } from '../../app/lib/server';
Meteor.methods({
@ -17,7 +17,15 @@ Meteor.methods({
}
const fromId = Meteor.userId();
const room = Meteor.call('canAccessRoom', rid, fromId);
const room = Rooms.findOneById(rid, { fields: { t: 1 } });
if (!room) {
return false;
}
if (!canAccessRoom(room, { _id: fromId })) {
return false;
}
if (!room) {
return false;

@ -1,8 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../app/models';
import { settings } from '../../app/settings';
import { canAccessRoom } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
import { settings } from '../../app/settings/server';
Meteor.methods({
loadMissedMessages(rid, start) {
@ -10,7 +11,12 @@ Meteor.methods({
check(start, Date);
const fromId = Meteor.userId();
if (!Meteor.call('canAccessRoom', rid, fromId)) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' });
}
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
return false;
}

@ -1,8 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../app/models';
import { settings } from '../../app/settings';
import { canAccessRoom } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
import { settings } from '../../app/settings/server';
import { normalizeMessagesForUser } from '../../app/utils/server/lib/normalizeMessagesForUser';
Meteor.methods({
@ -16,9 +17,13 @@ Meteor.methods({
});
}
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'loadNextMessages' });
}
const fromId = Meteor.userId();
if (!Meteor.call('canAccessRoom', rid, fromId)) {
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
return false;
}

@ -1,8 +1,9 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../app/models';
import { settings } from '../../app/settings';
import { canAccessRoom } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
import { settings } from '../../app/settings/server';
import { normalizeMessagesForUser } from '../../app/utils/server/lib/normalizeMessagesForUser';
Meteor.methods({
@ -28,7 +29,7 @@ Meteor.methods({
return false;
}
if (!Meteor.call('canAccessRoom', message.rid, fromId)) {
if (!canAccessRoom({ _id: message.rid }, { _id: fromId })) {
return false;
}

@ -2,9 +2,10 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { canAccessRoom } from '../../app/authorization/server';
import { Subscriptions } from '../../app/models/server';
import { Messages } from '../../app/models/server/raw';
import { settings } from '../../app/settings';
import { settings } from '../../app/settings/server';
import { readSecondaryPreferred } from '../database/readSecondaryPreferred';
Meteor.methods({
@ -30,8 +31,8 @@ Meteor.methods({
// Don't process anything else if the user can't access the room
if (rid) {
if (!Meteor.call('canAccessRoom', rid, currentUserId)) {
return result;
if (!canAccessRoom({ _id: rid }, { _id: currentUserId })) {
return false;
}
} else if (settings.get('Search.defaultProvider.GlobalSearchEnabled') !== true) {
return result;

@ -1,7 +1,8 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { Messages } from '../../app/models';
import { canAccessRoom } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
Meteor.methods({
'messages/get'(rid, { lastUpdate, latestDate = new Date(), oldestDate, inclusive = false, count = 20, unreads = false }) {
@ -15,7 +16,11 @@ Meteor.methods({
});
}
if (!Meteor.call('canAccessRoom', rid, fromId)) {
if (!rid) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'messages/get' });
}
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'messages/get',
});

@ -1,10 +1,10 @@
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import { roomTypes } from '../../../app/utils';
import { hasPermission } from '../../../app/authorization';
import { Rooms } from '../../../app/models';
import { settings } from '../../../app/settings';
import { roomTypes } from '../../../app/utils/server';
import { canAccessRoom, hasPermission } from '../../../app/authorization/server';
import { Rooms } from '../../../app/models/server';
import { settings } from '../../../app/settings/server';
import { roomFields } from '../../modules/watchers/publishFields';
const roomMap = (record) => {
@ -51,7 +51,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getRoomByTypeAndName' });
}
if (!Meteor.call('canAccessRoom', room._id, userId)) {
if (!canAccessRoom(room, { _id: userId })) {
throw new Meteor.Error('error-no-permission', 'No permission', { method: 'getRoomByTypeAndName' });
}

@ -750,7 +750,7 @@ describe('[Chat]', function() {
.to.have.string('<iframe style="max-width: 100%;width:400px;height:225px"');
})
.end(done);
}, 500);
}, 1000);
});
it('should embed an image preview if message has an image url', (done) => {
@ -1055,20 +1055,21 @@ describe('[Chat]', function() {
});
describe('/chat.search', () => {
beforeEach((done) => {
const sendMessage = (text) => {
request.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: 'GENERAL',
msg: text,
},
})
.end(() => {});
};
for (let i = 0; i < 5; i++) { sendMessage('msg1'); }
done();
before(async () => {
const sendMessage = (text) => request.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: 'GENERAL',
msg: text,
},
});
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
});
it('should return a list of messages when execute successfully', (done) => {

@ -710,8 +710,7 @@ describe('Meteor.methods', function() {
const data = JSON.parse(res.body.message);
expect(data).to.have.a.property('error').that.is.an('object');
expect(data.error).to.have.a.property('sanitizedError');
expect(data.error.sanitizedError).to.have.property('error', 400);
expect(data.error).to.have.a.property('error', 'error-invalid-room');
})
.end(done);
});

Loading…
Cancel
Save