Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions app/api/server/v1/chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Messages } from '../../../models';
import { canAccessRoom, hasPermission } from '../../../authorization';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { processWebhookMessage } from '../../../lib/server';
import { executeSendMessage } from '../../../lib/server/methods/sendMessage';
import { executeSetReaction } from '../../../reactions/server/setReaction';
import { API } from '../api';
import Rooms from '../../../models/server/models/Rooms';
import Users from '../../../models/server/models/Users';
Expand Down Expand Up @@ -172,8 +174,7 @@ API.v1.addRoute('chat.sendMessage', { authRequired: true }, {
throw new Meteor.Error('error-invalid-params', 'The "message" parameter must be provided.');
}

const sent = Meteor.runAsUser(this.userId, () => Meteor.call('sendMessage', this.bodyParams.message));

const sent = executeSendMessage(this.userId, this.bodyParams.message);
const [message] = normalizeMessagesForUser([sent], this.userId);

return API.v1.success({
Expand Down Expand Up @@ -294,7 +295,7 @@ API.v1.addRoute('chat.react', { authRequired: true }, {
throw new Meteor.Error('error-emoji-param-not-provided', 'The required "emoji" param is missing.');
}

Meteor.runAsUser(this.userId, () => Meteor.call('setReaction', emoji, msg._id, this.bodyParams.shouldReact));
Meteor.runAsUser(this.userId, () => Promise.await(executeSetReaction(emoji, msg._id, this.bodyParams.shouldReact)));

return API.v1.success();
},
Expand Down
18 changes: 14 additions & 4 deletions app/authorization/server/functions/canDeleteMessage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { hasPermissionAsync } from './hasPermission';
import { getValue } from '../../../settings/server/raw';
import { Rooms } from '../../../models';

const elapsedTime = (ts) => {
const dif = Date.now() - ts;
Expand Down Expand Up @@ -30,12 +31,21 @@ export const canDeleteMessageAsync = async (uid, { u, rid, ts }) => {
}
const blockDeleteInMinutes = await getValue('Message_AllowDeleting_BlockDeleteInMinutes');

if (!blockDeleteInMinutes) {
return true;
if (blockDeleteInMinutes) {
const timeElapsedForMessage = elapsedTime(ts);
return timeElapsedForMessage <= blockDeleteInMinutes;
}

const room = await Rooms.findOneById(rid, { fields: { ro: 1, unmuted: 1 } });
console.log(room, u.username);
Comment thread
pierre-lehnen-rc marked this conversation as resolved.
Outdated
if (room.ro === true && !await hasPermissionAsync(uid, 'post-readonly', rid)) {
// Unless the user was manually unmuted
if (!(room.unmuted || []).includes(u.username)) {
throw new Error('You can\'t delete messages because the room is readonly.');
}
}

const timeElapsedForMessage = elapsedTime(ts);
return timeElapsedForMessage <= blockDeleteInMinutes;
return true;
};

export const canDeleteMessage = (uid, { u, rid, ts }) => Promise.await(canDeleteMessageAsync(uid, { u, rid, ts }));
13 changes: 8 additions & 5 deletions app/authorization/server/functions/canSendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,19 @@ const subscriptionOptions = {
},
};

export const canSendMessageAsync = async (rid, { uid, username, type }, extraData) => {
const room = await Rooms.findOneById(rid);

export const validateRoomMessagePermissionsAsync = async (room, { uid, username, type }, extraData) => {
if (type !== 'app' && !await canAccessRoomAsync(room, { _id: uid, username }, extraData)) {
throw new Error('error-not-allowed');
}

if (roomTypes.getConfig(room.t).allowMemberAction(room, RoomMemberActions.BLOCK)) {
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, uid, subscriptionOptions);
const subscription = await Subscriptions.findOneByRoomIdAndUserId(room._id, uid, subscriptionOptions);
if (subscription && (subscription.blocked || subscription.blocker)) {
throw new Error('room_is_blocked');
}
}

if (room.ro === true && !await hasPermissionAsync(uid, 'post-readonly', rid)) {
if (room.ro === true && !await hasPermissionAsync(uid, 'post-readonly', room._id)) {
// Unless the user was manually unmuted
if (!(room.unmuted || []).includes(username)) {
throw new Error('You can\'t send messages because the room is readonly.');
Expand All @@ -34,8 +32,13 @@ export const canSendMessageAsync = async (rid, { uid, username, type }, extraDat
if ((room.muted || []).includes(username)) {
throw new Error('You_have_been_muted');
}
};

export const canSendMessageAsync = async (rid, { uid, username, type }, extraData) => {
const room = await Rooms.findOneById(rid);
await validateRoomMessagePermissionsAsync(room, { uid, username, type }, extraData);
return room;
};

export const canSendMessage = (rid, { uid, username, type }, extraData) => Promise.await(canSendMessageAsync(rid, { uid, username, type }, extraData));
export const validateRoomMessagePermissions = (room, { uid, username, type }, extraData) => Promise.await(validateRoomMessagePermissionsAsync(room, { uid, username, type }, extraData));
3 changes: 2 additions & 1 deletion app/authorization/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
canAccessRoom,
roomAccessValidators,
} from './functions/canAccessRoom';
import { canSendMessage } from './functions/canSendMessage';
import { canSendMessage, validateRoomMessagePermissions } from './functions/canSendMessage';
import { getRoles } from './functions/getRoles';
import { getUsersInRole } from './functions/getUsersInRole';
import {
Expand All @@ -30,6 +30,7 @@ export {
hasRole,
removeUserFromRoles,
canSendMessage,
validateRoomMessagePermissions,
addRoomAccessValidator,
roomAccessValidators,
addUserRoles,
Expand Down
3 changes: 3 additions & 0 deletions app/lib/server/functions/processWebhookMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import s from 'underscore.string';

import { getRoomByNameOrIdWithOptionToJoin } from './getRoomByNameOrIdWithOptionToJoin';
import { sendMessage } from './sendMessage';
import { validateRoomMessagePermissions } from '../../../authorization/server/functions/canSendMessage';
import { Subscriptions } from '../../../models';
import { getDirectMessageByIdWithOptionToJoin, getDirectMessageByNameOrIdWithOptionToJoin } from './getDirectMessageByNameOrIdWithOptionToJoin';

Expand Down Expand Up @@ -82,6 +83,8 @@ export const processWebhookMessage = function(messageObj, user, defaultValues =
}
}

validateRoomMessagePermissions(room, { uid: user._id, ...user });

const messageReturn = sendMessage(user, message, room);
sentData.push({ channel, message: messageReturn });
}
Expand Down
23 changes: 17 additions & 6 deletions app/lib/server/methods/sendMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,21 @@ export function executeSendMessage(uid, message) {
metrics.messagesSent.inc(); // TODO This line needs to be moved to it's proper place. See the comments on: https://github.com/RocketChat/Rocket.Chat/pull/5736
return sendMessage(user, message, room, false);
} catch (error) {
if (error === 'error-not-allowed') {
throw new Meteor.Error('error-not-allowed');
}

SystemLogger.error('Error sending message:', error);

const errorMessage = typeof error === 'string' ? error : error.error || error.message;
Notifications.notifyUser(uid, 'message', {
_id: Random.id(),
rid: message.rid,
ts: new Date(),
msg: TAPi18n.__(error, {}, user.language),
msg: TAPi18n.__(errorMessage, {}, user.language),
});

if (typeof error === 'string') {
throw new Error(error);
}

throw error;
}
}

Expand All @@ -98,7 +101,15 @@ Meteor.methods({
});
}

return executeSendMessage(uid, message);
try {
return executeSendMessage(uid, message);
} catch (error) {
if ((error.error || error.message) === 'error-not-allowed') {
throw new Meteor.Error(error.error || error.message, error.reason, {
method: 'sendMessage',
});
}
}
},
});
// Limit a user, who does not have the "bot" role, to sending 5 msgs/second
Expand Down
5 changes: 4 additions & 1 deletion app/lib/server/methods/updateMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import moment from 'moment';

import { Messages } from '../../../models';
import { settings } from '../../../settings';
import { hasPermission } from '../../../authorization';
import { hasPermission, canSendMessage } from '../../../authorization/server';
import { updateMessage } from '../functions';

Meteor.methods({
Expand Down Expand Up @@ -47,6 +47,9 @@ Meteor.methods({
}
}

const user = Meteor.users.findOne(Meteor.userId());
canSendMessage(message.rid, { uid: user._id, ...user });

// It is possible to have an empty array as the attachments property, so ensure both things exist
if (originalMessage.attachments && originalMessage.attachments.length > 0 && originalMessage.attachments[0].description !== undefined) {
message.attachments = originalMessage.attachments;
Expand Down
55 changes: 35 additions & 20 deletions app/reactions/server/setReaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,9 @@ async function setReaction(room, user, message, reaction, shouldReact) {
}

if (Array.isArray(room.muted) && room.muted.indexOf(user.username) !== -1) {
Notifications.notifyUser(Meteor.userId(), 'message', {
_id: Random.id(),
throw new Meteor.Error('error-not-allowed', TAPi18n.__('You_have_been_muted', {}, user.language), {
rid: room._id,
ts: new Date(),
msg: TAPi18n.__('You_have_been_muted', {}, user.language),
});
return false;
}

const userAlreadyReacted = Boolean(message.reactions) && Boolean(message.reactions[reaction]) && message.reactions[reaction].usernames.indexOf(user.username) !== -1;
Expand Down Expand Up @@ -88,26 +84,45 @@ async function setReaction(room, user, message, reaction, shouldReact) {
msgStream.emit(message.rid, message);
}

Meteor.methods({
setReaction(reaction, messageId, shouldReact) {
const user = Meteor.user();
export const executeSetReaction = async function(reaction, messageId, shouldReact) {
const user = Meteor.user();

if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' });
}
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' });
}

const message = Messages.findOneById(messageId);
const message = Messages.findOneById(messageId);

if (!message) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}
if (!message) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}

const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());

if (!room) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}
if (!room) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
}

setReaction(room, user, message, reaction, shouldReact);
return setReaction(room, user, message, reaction, shouldReact);
};

Meteor.methods({
setReaction(reaction, messageId, shouldReact) {
try {
return Promise.await(executeSetReaction(reaction, messageId, shouldReact));
} catch (e) {
if (e.error === 'error-not-allowed' && e.reason && e.details && e.details.rid) {
Notifications.notifyUser(Meteor.userId(), 'message', {
_id: Random.id(),
rid: e.details.rid,
ts: new Date(),
msg: e.reason,
});

return false;
}

throw e;
}
},
});