[FIX] Inconsistent validation of user's access to rooms (#24037)

* [FIX] Inconsistent validation of user's access to rooms

* Replaced Object.assign with object destructuring

* prettier

* packages

* type

* Fixed invalid import from micro services

* Removed code duplicated by merge conflict

* Adjusted new validator

Co-authored-by: Leonardo Ostjen Couto <leonardoostjen@gmail.com>
pull/24393/head
pierre-lehnen-rc 4 years ago committed by GitHub
parent 8eaf205180
commit 2f405cb944
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      app/api/server/v1/chat.js
  2. 8
      app/api/server/v1/commands.js
  3. 9
      app/api/server/v1/groups.js
  4. 4
      app/api/server/v1/rooms.js
  5. 10
      app/authorization/server/functions/canAccessRoom.ts
  6. 4
      app/authorization/server/index.js
  7. 8
      app/e2e/server/methods/getUsersOfRoomWithoutKey.js
  8. 4
      app/e2e/server/methods/setRoomKeyID.js
  9. 6
      app/lib/server/methods/getChannelHistory.ts
  10. 7
      app/lib/server/methods/getMessages.ts
  11. 4
      app/lib/server/methods/getSingleMessage.ts
  12. 12
      app/livechat/server/lib/Livechat.js
  13. 4
      app/livechat/server/roomAccessValidator.internalService.ts
  14. 4
      app/message-pin/server/pinMessage.js
  15. 5
      app/message-star/server/starMessage.js
  16. 4
      app/threads/server/methods/followMessage.js
  17. 4
      app/threads/server/methods/unfollowMessage.js
  18. 2
      app/tokenpass/server/roomAccessValidator.internalService.ts
  19. 4
      app/videobridge/server/methods/bbb.js
  20. 10
      ee/server/services/package-lock.json
  21. 2
      ee/server/services/package.json
  22. 4
      imports/message-read-receipt/server/api/methods/getReadReceipts.js
  23. 4
      server/lib/spotlight.js
  24. 4
      server/methods/getUsersOfRoom.js
  25. 4
      server/methods/loadHistory.js
  26. 4
      server/methods/loadMissedMessages.js
  27. 4
      server/methods/loadNextMessages.js
  28. 4
      server/methods/loadSurroundingMessages.js
  29. 4
      server/methods/messageSearch.js
  30. 4
      server/publications/messages.js
  31. 7
      server/sdk/types/IAuthorization.ts
  32. 5
      server/sdk/types/IAuthorizationLivechat.ts
  33. 16
      server/services/authorization/canAccessRoomLivechat.ts
  34. 19
      server/services/authorization/service.ts

@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { Messages } from '../../../models';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { canAccessRoom, canAccessRoomId, roomAccessAttributes, hasPermission } from '../../../authorization/server';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { processWebhookMessage } from '../../../lib/server';
import { executeSendMessage } from '../../../lib/server/methods/sendMessage';
@ -496,7 +496,7 @@ API.v1.addRoute(
throw new Meteor.Error('error-roomId-param-not-provided', 'The required "roomId" query param is missing.');
}
if (!canAccessRoom({ _id: roomId }, { _id: this.userId })) {
if (!canAccessRoomId(roomId, this.userId)) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
@ -535,7 +535,8 @@ API.v1.addRoute(
throw new Meteor.Error('error-not-allowed', 'Threads Disabled');
}
const user = Users.findOneById(this.userId, { fields: { _id: 1 } });
const room = Rooms.findOneById(rid, { fields: { t: 1, _id: 1 } });
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } });
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
}
@ -543,9 +544,7 @@ API.v1.addRoute(
const typeThread = {
_hidden: { $ne: true },
...(type === 'following' && { replies: { $in: [this.userId] } }),
...(type === 'unread' && {
_id: { $in: Subscriptions.findOneByRoomIdAndUserId(room._id, user._id).tunread },
}),
...(type === 'unread' && { _id: { $in: Subscriptions.findOneByRoomIdAndUserId(room._id, user._id).tunread } }),
msg: new RegExp(escapeRegExp(text), 'i'),
};
@ -595,7 +594,8 @@ API.v1.addRoute(
updatedSinceDate = new Date(updatedSince);
}
const user = Users.findOneById(this.userId, { fields: { _id: 1 } });
const room = Rooms.findOneById(rid, { fields: { t: 1, _id: 1 } });
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } });
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
}
@ -603,10 +603,7 @@ API.v1.addRoute(
return API.v1.success({
threads: {
update: Messages.find({ ...threadQuery, _updatedAt: { $gt: updatedSinceDate } }, { fields, sort }).fetch(),
remove: Messages.trashFindDeletedAfter(updatedSinceDate, threadQuery, {
fields,
sort,
}).fetch(),
remove: Messages.trashFindDeletedAfter(updatedSinceDate, threadQuery, { fields, sort }).fetch(),
},
});
},
@ -633,7 +630,7 @@ API.v1.addRoute(
throw new Meteor.Error('error-invalid-message', 'Invalid Message');
}
const user = Users.findOneById(this.userId, { fields: { _id: 1 } });
const room = Rooms.findOneById(thread.rid, { fields: { t: 1, _id: 1 } });
const room = Rooms.findOneById(thread.rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } });
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');
@ -690,7 +687,7 @@ API.v1.addRoute(
throw new Meteor.Error('error-invalid-message', 'Invalid Message');
}
const user = Users.findOneById(this.userId, { fields: { _id: 1 } });
const room = Rooms.findOneById(thread.rid, { fields: { t: 1, _id: 1 } });
const room = Rooms.findOneById(thread.rid, { fields: { ...roomAccessAttributes, t: 1, _id: 1 } });
if (!canAccessRoom(room, user)) {
throw new Meteor.Error('error-not-allowed', 'Not Allowed');

@ -4,7 +4,7 @@ import objectPath from 'object-path';
import { slashCommands } from '../../../utils/server';
import { Messages } from '../../../models/server';
import { canAccessRoom } from '../../../authorization/server';
import { canAccessRoomId } from '../../../authorization/server';
import { API } from '../api';
API.v1.addRoute(
@ -201,7 +201,7 @@ API.v1.addRoute(
return API.v1.failure('The command provided does not exist (or is disabled).');
}
if (!canAccessRoom({ _id: body.roomId }, user)) {
if (!canAccessRoomId(body.roomId, user._id)) {
return API.v1.unauthorized();
}
@ -255,7 +255,7 @@ API.v1.addRoute(
return API.v1.failure('The command provided does not exist (or is disabled).');
}
if (!canAccessRoom({ _id: query.roomId }, user)) {
if (!canAccessRoomId(query.roomId, user._id)) {
return API.v1.unauthorized();
}
@ -310,7 +310,7 @@ API.v1.addRoute(
return API.v1.failure('The command provided does not exist (or is disabled).');
}
if (!canAccessRoom({ _id: body.roomId }, user)) {
if (!canAccessRoomId(body.roomId, user._id)) {
return API.v1.unauthorized();
}

@ -5,7 +5,13 @@ import { Match, check } from 'meteor/check';
import { mountIntegrationQueryBasedOnPermissions } from '../../../integrations/server/lib/mountQueriesBasedOnPermission';
import { Subscriptions, Rooms, Messages, Users } from '../../../models/server';
import { Integrations, Uploads } from '../../../models/server/raw';
import { hasPermission, hasAtLeastOnePermission, canAccessRoom, hasAllPermission } from '../../../authorization/server';
import {
hasPermission,
hasAtLeastOnePermission,
canAccessRoom,
hasAllPermission,
roomAccessAttributes,
} from '../../../authorization/server';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import { API } from '../api';
import { Team } from '../../../../server/sdk';
@ -19,6 +25,7 @@ export function findPrivateGroupByIdOrName({ params, userId, checkedArchived = t
const roomOptions = {
fields: {
...roomAccessAttributes,
t: 1,
ro: 1,
name: 1,

@ -12,7 +12,7 @@ import {
findChannelAndPrivateAutocompleteWithPagination,
} from '../lib/rooms';
import { sendFile, sendViaEmail } from '../../../../server/lib/channelExport';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { canAccessRoom, canAccessRoomId, hasPermission } from '../../../authorization/server';
import { Media } from '../../../../server/sdk';
import { settings } from '../../../settings/server/index';
import { getUploadFormData } from '../lib/getUploadFormData';
@ -81,7 +81,7 @@ API.v1.addRoute(
{ authRequired: true },
{
post() {
if (!canAccessRoom({ _id: this.urlParams.rid }, { _id: this.userId })) {
if (!canAccessRoomId(this.urlParams.rid, this.userId)) {
return API.v1.unauthorized();
}

@ -2,5 +2,15 @@ import { Authorization } from '../../../../server/sdk';
import { IAuthorization } from '../../../../server/sdk/types/IAuthorization';
export const canAccessRoomAsync = Authorization.canAccessRoom;
export const canAccessRoomIdAsync = Authorization.canAccessRoomId;
export const roomAccessAttributes = {
_id: 1,
t: 1,
teamId: 1,
prid: 1,
tokenpass: 1,
};
export const canAccessRoom = (...args: Parameters<IAuthorization['canAccessRoom']>): boolean => Promise.await(canAccessRoomAsync(...args));
export const canAccessRoomId = (...args: Parameters<IAuthorization['canAccessRoomId']>): boolean =>
Promise.await(canAccessRoomIdAsync(...args));

@ -1,5 +1,5 @@
import { addUserRoles } from './functions/addUserRoles';
import { canAccessRoom, roomAccessValidators } from './functions/canAccessRoom';
import { canAccessRoom, canAccessRoomId, roomAccessAttributes, roomAccessValidators } from './functions/canAccessRoom';
import { canSendMessage, validateRoomMessagePermissions } from './functions/canSendMessage';
import { getRoles } from './functions/getRoles';
import { getUsersInRole } from './functions/getUsersInRole';
@ -26,6 +26,8 @@ export {
roomAccessValidators,
addUserRoles,
canAccessRoom,
canAccessRoomId,
roomAccessAttributes,
hasAllPermission,
hasAtLeastOnePermission,
hasPermission,

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../../authorization/server';
import { canAccessRoomId } from '../../../authorization/server';
import { Subscriptions, Users } from '../../../models/server';
Meteor.methods({
@ -21,10 +21,8 @@ Meteor.methods({
});
}
if (!canAccessRoom({ _id: rid }, { _id: userId })) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'e2e.getUsersOfRoomWithoutKey',
});
if (!canAccessRoomId(rid, userId)) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' });
}
const subscriptions = Subscriptions.findByRidWithoutE2EKey(rid, {

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../../authorization/server';
import { canAccessRoomId } from '../../../authorization/server';
import { Rooms } from '../../../models/server';
Meteor.methods({
@ -18,7 +18,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
}
if (!canAccessRoom({ _id: rid }, { _id: userId })) {
if (!canAccessRoomId(rid, userId)) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' });
}

@ -16,7 +16,11 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'getChannelHistory' });
}
const fromUserId = Meteor.userId() || undefined;
const fromUserId = Meteor.userId();
if (!fromUserId) {
return false;
}
const room = Rooms.findOneById(rid);
if (!room) {
return false;

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../../authorization/server';
import { canAccessRoomId } from '../../../authorization/server';
import { Messages } from '../../../models/server';
import { IMessage } from '../../../../definition/IMessage';
@ -15,12 +15,9 @@ Meteor.methods({
}
const msgs = Messages.findVisibleByIds(messages).fetch() as IMessage[];
const user = { _id: uid };
const rids = [...new Set(msgs.map((m) => m.rid))];
if (!rids.every((_id) => canAccessRoom({ _id }, user))) {
if (!rids.every((_id) => canAccessRoomId(_id, uid))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getSingleMessage' });
}

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

@ -30,7 +30,14 @@ import {
LivechatInquiry,
} from '../../../models/server';
import { Logger } from '../../../logger/server';
import { addUserRoles, hasPermission, hasRole, removeUserFromRoles, canAccessRoom } from '../../../authorization/server';
import {
addUserRoles,
hasPermission,
hasRole,
removeUserFromRoles,
canAccessRoom,
roomAccessAttributes,
} from '../../../authorization/server';
import * as Mailer from '../../../mailer';
import { sendMessage } from '../../../lib/server/functions/sendMessage';
import { updateMessage } from '../../../lib/server/functions/updateMessage';
@ -1378,7 +1385,8 @@ export const Livechat = {
throw new Error('error-not-authorized');
}
const room = Promise.await(LivechatRooms.findOneById(roomId, { _id: 1, t: 1 }));
const room = Promise.await(LivechatRooms.findOneById(roomId, { ...roomAccessAttributes, _id: 1, t: 1 }));
if (!room) {
throw new Meteor.Error('invalid-room');
}

@ -2,7 +2,7 @@ import { ServiceClass } from '../../../server/sdk/types/ServiceClass';
import { IAuthorizationLivechat } from '../../../server/sdk/types/IAuthorizationLivechat';
import { validators } from './roomAccessValidator.compatibility';
import { api } from '../../../server/sdk/api';
import { IRoom } from '../../../definition/IRoom';
import type { IOmnichannelRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';
class AuthorizationLivechat extends ServiceClass implements IAuthorizationLivechat {
@ -10,7 +10,7 @@ class AuthorizationLivechat extends ServiceClass implements IAuthorizationLivech
protected internal = true;
async canAccessRoom(room: Partial<IRoom>, user: Partial<IUser>, extraData?: object): Promise<boolean> {
async canAccessRoom(room: IOmnichannelRoom, user: Pick<IUser, '_id'>, extraData?: object): Promise<boolean> {
for (const validator of validators) {
if (validator(room, user, extraData)) {
return true;

@ -5,7 +5,7 @@ import { settings } from '../../settings/server';
import { callbacks } from '../../../lib/callbacks';
import { isTheLastMessage } from '../../lib/server';
import { getUserAvatarURL } from '../../utils/lib/getUserAvatarURL';
import { canAccessRoom, hasPermission } from '../../authorization/server';
import { canAccessRoom, hasPermission, roomAccessAttributes } from '../../authorization/server';
import { Subscriptions, Messages, Users, Rooms } from '../../models';
const recursiveRemove = (msg, deep = 1) => {
@ -164,7 +164,7 @@ Meteor.methods({
};
originalMessage = callbacks.run('beforeSaveMessage', originalMessage);
const room = Rooms.findOneById(originalMessage.rid, { fields: { lastMessage: 1 } });
const room = Rooms.findOneById(originalMessage.rid, { fields: { ...roomAccessAttributes, lastMessage: 1 } });
if (!canAccessRoom(room, { _id: Meteor.userId() })) {
throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'unpinMessage' });
}

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

@ -4,7 +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 { canAccessRoomId } from '../../../authorization/server';
import { follow } from '../functions';
Meteor.methods({
@ -27,7 +27,7 @@ Meteor.methods({
});
}
if (!canAccessRoom({ _id: message.rid }, { _id: uid })) {
if (!canAccessRoomId(message.rid, uid)) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'followMessage' });
}

@ -4,7 +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 { canAccessRoomId } from '../../../authorization/server';
import { unfollow } from '../functions';
Meteor.methods({
@ -27,7 +27,7 @@ Meteor.methods({
});
}
if (!canAccessRoom({ _id: message.rid }, { _id: uid })) {
if (!canAccessRoomId(message.rid, uid)) {
throw new Meteor.Error('error-not-allowed', 'not-allowed', { method: 'unfollowMessage' });
}

@ -10,7 +10,7 @@ class AuthorizationTokenpass extends ServiceClass implements IAuthorizationToken
protected internal = true;
async canAccessRoom(room: Partial<IRoom>, user: Partial<IUser>): Promise<boolean> {
async canAccessRoom(room: Pick<IRoom, '_id' | 't' | 'teamId' | 'prid' | 'tokenpass'>, user: Pick<IUser, '_id'>): Promise<boolean> {
for (const validator of validators) {
if (validator(room, user)) {
return true;

@ -8,7 +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 { canAccessRoom, canAccessRoomId } from '../../../authorization/server';
import { API } from '../../../api/server';
const parser = new xml2js.Parser({
@ -115,7 +115,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'bbbEnd' });
}
if (!canAccessRoom({ _id: rid }, { _id: this.userId })) {
if (!canAccessRoomId(rid, this.userId)) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'bbbEnd' });
}

@ -233,6 +233,11 @@
"resolved": "https://registry.npmjs.org/@rocket.chat/emitter/-/emitter-0.30.1.tgz",
"integrity": "sha512-BH1wMBo5AZwgWXIRm4k2M5rz9W7uDR+2xbfo/HP2bIjyTTq9mlU/20w0JBXAR7PaMf8gWgyfAWWGPi4ZBHA+ag=="
},
"@rocket.chat/message-parser": {
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/@rocket.chat/message-parser/-/message-parser-0.31.2.tgz",
"integrity": "sha512-T1ZKxAC8YgrZyb5lRQTu5rb79SUMaxvhhMhmdQpnMyqU5pPJ7UuqucvheUWxpZQY1Q9tHha+OCVacU7O/CocSw=="
},
"@rocket.chat/string-helpers": {
"version": "0.30.1",
"resolved": "https://registry.npmjs.org/@rocket.chat/string-helpers/-/string-helpers-0.30.1.tgz",
@ -241,6 +246,11 @@
"tslib": "^2.3.0"
}
},
"@rocket.chat/ui-kit": {
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/@rocket.chat/ui-kit/-/ui-kit-0.31.2.tgz",
"integrity": "sha512-uzLh2rYbjG2TYTn18iDwJ7NewD2kJHVYofMIJhR11Ij6oAMezDgdBZjZEE5CZ3SF7027byGBETc5npG83sABWg=="
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",

@ -23,7 +23,9 @@
"license": "MIT",
"dependencies": {
"@rocket.chat/emitter": "^0.30.1",
"@rocket.chat/message-parser": "^0.31.2",
"@rocket.chat/string-helpers": "^0.30.1",
"@rocket.chat/ui-kit": "^0.31.2",
"ajv": "^8.7.1",
"bcrypt": "^5.0.1",
"body-parser": "^1.19.0",

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

@ -1,7 +1,7 @@
import s from 'underscore.string';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { hasAllPermission, hasPermission, canAccessRoom } from '../../app/authorization/server';
import { hasAllPermission, hasPermission, canAccessRoom, roomAccessAttributes } from '../../app/authorization/server';
import { Subscriptions, Rooms } from '../../app/models/server';
import { Users } from '../../app/models/server/raw';
import { settings } from '../../app/settings/server';
@ -137,7 +137,7 @@ export class Spotlight {
readPreference: readSecondaryPreferred(Users.col.s.db),
};
const room = Rooms.findOneById(rid, { fields: { _id: 1, t: 1, uids: 1 } });
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, _id: 1, t: 1, uids: 1 } });
if (rid && !room) {
return users;

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Subscriptions, Rooms } from '../../app/models/server';
import { canAccessRoom, hasPermission } from '../../app/authorization/server';
import { canAccessRoom, hasPermission, roomAccessAttributes } from '../../app/authorization/server';
import { findUsersOfRoom } from '../lib/findUsersOfRoom';
Meteor.methods({
@ -15,7 +15,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' });
}
const room = Rooms.findOneById(rid, { fields: { broadcast: 1 } });
const room = Rooms.findOneById(rid, { fields: { ...roomAccessAttributes, broadcast: 1 } });
if (!room) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getUsersOfRoom' });
}

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

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../app/authorization/server';
import { canAccessRoomId } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
import { settings } from '../../app/settings/server';
@ -16,7 +16,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'getUsersOfRoom' });
}
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
if (!canAccessRoomId(rid, fromId)) {
return false;
}

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../app/authorization/server';
import { canAccessRoomId } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
import { settings } from '../../app/settings/server';
import { normalizeMessagesForUser } from '../../app/utils/server/lib/normalizeMessagesForUser';
@ -23,7 +23,7 @@ Meteor.methods({
const fromId = Meteor.userId();
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
if (!canAccessRoomId(rid, fromId)) {
return false;
}

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

@ -2,7 +2,7 @@ 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 { canAccessRoomId } from '../../app/authorization/server';
import { Subscriptions } from '../../app/models/server';
import { Messages } from '../../app/models/server/raw';
import { settings } from '../../app/settings/server';
@ -31,7 +31,7 @@ Meteor.methods({
// Don't process anything else if the user can't access the room
if (rid) {
if (!canAccessRoom({ _id: rid }, { _id: currentUserId })) {
if (!canAccessRoomId(rid, currentUserId)) {
return false;
}
} else if (settings.get('Search.defaultProvider.GlobalSearchEnabled') !== true) {

@ -1,7 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { canAccessRoom } from '../../app/authorization/server';
import { canAccessRoomId } from '../../app/authorization/server';
import { Messages } from '../../app/models/server';
Meteor.methods({
@ -20,7 +20,7 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'messages/get' });
}
if (!canAccessRoom({ _id: rid }, { _id: fromId })) {
if (!canAccessRoomId(rid, fromId)) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'messages/get',
});

@ -1,7 +1,11 @@
import { IRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';
export type RoomAccessValidator = (room: Partial<IRoom>, user: Partial<IUser>, extraData?: Record<string, any>) => Promise<boolean>;
export type RoomAccessValidator = (
room: Pick<IRoom, '_id' | 't' | 'teamId' | 'prid' | 'tokenpass'>,
user: Pick<IUser, '_id'>,
extraData?: Record<string, any>,
) => Promise<boolean>;
export interface IAuthorization {
hasAllPermission(userId: string, permissions: string[], scope?: string): Promise<boolean>;
@ -9,4 +13,5 @@ export interface IAuthorization {
hasAtLeastOnePermission(userId: string, permissions: string[], scope?: string): Promise<boolean>;
addRoleRestrictions(role: string, permissions: string[]): Promise<void>;
canAccessRoom: RoomAccessValidator;
canAccessRoomId(rid: IRoom['_id'], uid: IUser['_id']): Promise<boolean>;
}

@ -1,5 +1,6 @@
import { RoomAccessValidator } from './IAuthorization';
import type { IOmnichannelRoom } from '../../../definition/IRoom';
import type { IUser } from '../../../definition/IUser';
export interface IAuthorizationLivechat {
canAccessRoom: RoomAccessValidator;
canAccessRoom: (room: IOmnichannelRoom, user: Pick<IUser, '_id'>, extraData?: Record<string, any>) => Promise<boolean>;
}

@ -2,18 +2,26 @@ import { IAuthorizationLivechat } from '../../sdk/types/IAuthorizationLivechat';
import { proxifyWithWait } from '../../sdk/lib/proxify';
import { RoomAccessValidator } from '../../sdk/types/IAuthorization';
import { Rooms } from './service';
import type { IOmnichannelRoom } from '../../../definition/IRoom';
export const AuthorizationLivechat = proxifyWithWait<IAuthorizationLivechat>('authorization-livechat');
export const canAccessRoomLivechat: RoomAccessValidator = async (room, user, extraData): Promise<boolean> => {
// room can be sent as `null` but in that case a `rid` is also sent on extraData
// this is the case for file uploads
const livechatRoom = room || (extraData?.rid && (await Rooms.findOneById(extraData?.rid)));
// If we received a partial room and its type is not `l`, skip all checks.
if (room && room.t !== 'l') {
return false;
}
// if we received a partial room, load the full IOmnichannelRoom data for it
// Otherwise, try to load the data from the extraData (this is the case for file uploads)
const rid = room?._id || extraData?.rid;
const livechatRoom = rid && (await Rooms.findOneById(rid));
// Check the type again in case the room parameter was not received
if (livechatRoom?.t !== 'l') {
return false;
}
// Call back core temporarily
return AuthorizationLivechat.canAccessRoom(livechatRoom, user, extraData);
return AuthorizationLivechat.canAccessRoom(livechatRoom as IOmnichannelRoom, user, extraData);
};

@ -14,6 +14,7 @@ import { TeamRaw } from '../../../app/models/server/raw/Team';
import { RolesRaw } from '../../../app/models/server/raw/Roles';
import { UsersRaw } from '../../../app/models/server/raw/Users';
import { IRole } from '../../../definition/IRole';
import type { IRoom } from '../../../definition/IRoom';
import { ISubscription } from '../../../definition/ISubscription';
import './canAccessRoomLivechat';
@ -100,6 +101,24 @@ export class Authorization extends ServiceClass implements IAuthorization {
return canAccessRoom(...args);
}
async canAccessRoomId(rid: IRoom['_id'], uid: IUser['_id']): Promise<boolean> {
const room = await Rooms.findOneById<Pick<IRoom, '_id' | 't' | 'teamId' | 'prid' | 'tokenpass'>>(rid, {
projection: {
_id: 1,
t: 1,
teamId: 1,
prid: 1,
tokenpass: 1,
},
});
if (!room) {
return false;
}
return this.canAccessRoom(room, { _id: uid });
}
async addRoleRestrictions(role: string, permissions: string[]): Promise<void> {
AuthorizationUtils.addRolePermissionWhiteList(role, permissions);
}

Loading…
Cancel
Save