Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/dull-trainers-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

fix: Remove model-level query restrictions for monitors
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export const Livechat = {
Livechat.logger.debug(`Closing open chats for user ${userId}`);
const user = await Users.findOneById(userId);

const extraQuery = await callbacks.run('livechat.applyDepartmentRestrictions', {});
const extraQuery = await callbacks.run('livechat.applyDepartmentRestrictions', {}, { userId });
const openChats = LivechatRooms.findOpenByAgent(userId, extraQuery);
const promises = [];
await openChats.forEach((room) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import type { FilterOperators } from 'mongodb';
import { hasRoleAsync } from '../../../../../app/authorization/server/functions/hasRole';
import { callbacks } from '../../../../../lib/callbacks';
import { cbLogger } from '../lib/logger';
import { getUnitsFromUser } from '../lib/units';
import { getUnitsFromUser } from '../methods/getUnitsFromUserRoles';

export const addQueryRestrictionsToDepartmentsModel = async (originalQuery: FilterOperators<ILivechatDepartment> = {}) => {
export const addQueryRestrictionsToDepartmentsModel = async (originalQuery: FilterOperators<ILivechatDepartment> = {}, userId: string) => {
const query: FilterOperators<ILivechatDepartment> = { ...originalQuery, type: { $ne: 'u' } };

const units = await getUnitsFromUser();
const units = await getUnitsFromUser(userId);
if (Array.isArray(units)) {
query.ancestors = { $in: units };
}

cbLogger.debug({ msg: 'Applying department query restrictions', userId, units });
return query;
};

Expand All @@ -25,7 +26,7 @@ callbacks.add(
}

cbLogger.debug('Applying department query restrictions');
return addQueryRestrictionsToDepartmentsModel(originalQuery);
return addQueryRestrictionsToDepartmentsModel(originalQuery, userId);
Comment thread
KevLehman marked this conversation as resolved.
},
callbacks.priority.HIGH,
'livechat-apply-department-restrictions',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { LivechatDepartment } from '@rocket.chat/models';
import type { FilterOperators } from 'mongodb';

import { callbacks } from '../../../../../lib/callbacks';
import { cbLogger } from '../lib/logger';
import { getUnitsFromUser } from '../lib/units';

export const restrictQuery = async (originalQuery: FilterOperators<IOmnichannelRoom> = {}) => {
Expand All @@ -20,6 +21,7 @@ export const restrictQuery = async (originalQuery: FilterOperators<IOmnichannelR
};
query.$and = [condition, ...expressions];

cbLogger.debug({ msg: 'Applying room query restrictions', units });
return query;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { LivechatUnit } from '@rocket.chat/models';
import { LivechatUnit, LivechatDepartmentAgents } from '@rocket.chat/models';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import mem from 'mem';
import { Meteor } from 'meteor/meteor';

import { hasAnyRoleAsync } from '../../../../../app/authorization/server/functions/hasRole';
import { logger } from '../lib/logger';

async function getUnitsFromUserRoles(user: string | null): Promise<string[] | undefined> {
async function getUnitsFromUserRoles(user: string): Promise<string[]> {
return LivechatUnit.findByMonitorId(user);
}

async function getDepartmentsFromUserRoles(user: string): Promise<string[]> {
return (await LivechatDepartmentAgents.findByAgentId(user).toArray()).map((department) => department.departmentId);
}

const memoizedGetUnitFromUserRoles = mem(getUnitsFromUserRoles, { maxAge: 10000 });
const memoizedGetDepartmentsFromUserRoles = mem(getDepartmentsFromUserRoles, { maxAge: 5000 });

export const getUnitsFromUser = async (user: string): Promise<string[] | undefined> => {
if (!user || (await hasAnyRoleAsync(user, ['admin', 'livechat-manager']))) {
return;
}
Expand All @@ -14,10 +26,11 @@ async function getUnitsFromUserRoles(user: string | null): Promise<string[] | un
return;
}

return LivechatUnit.findByMonitorId(user);
}
const unitsAndDepartments = [...(await memoizedGetUnitFromUserRoles(user)), ...(await memoizedGetDepartmentsFromUserRoles(user))];
logger.debug({ msg: 'Calculating units for monitor', user, unitsAndDepartments });

const memoizedGetUnitFromUserRoles = mem(getUnitsFromUserRoles, { maxAge: 10000 });
return unitsAndDepartments;
};

declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand All @@ -27,8 +40,11 @@ declare module '@rocket.chat/ui-contexts' {
}

Meteor.methods<ServerMethods>({
'livechat:getUnitsFromUser'(): Promise<string[] | undefined> {
async 'livechat:getUnitsFromUser'(): Promise<string[] | undefined> {
const user = Meteor.userId();
return memoizedGetUnitFromUserRoles(user);
if (!user) {
return;
}
return getUnitsFromUser(user);
},
});
27 changes: 0 additions & 27 deletions apps/meteor/ee/server/models/raw/LivechatRooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type { FindCursor, UpdateResult, Document, FindOptions, Db, Collection, F

import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred';
import { LivechatRoomsRaw } from '../../../../server/models/raw/LivechatRooms';
import { addQueryRestrictionsToRoomsModel } from '../../../app/livechat-enterprise/server/lib/query.helper';

declare module '@rocket.chat/model-typings' {
interface ILivechatRoomsModel {
Expand Down Expand Up @@ -296,32 +295,6 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo
return this.updateOne(query, update);
}

/** @deprecated Use updateOne or updateMany instead */
async update(...args: Parameters<LivechatRoomsRaw['update']>) {
const [query, ...restArgs] = args;
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
return super.update(restrictedQuery, ...restArgs);
}

async updateOne(...args: [...Parameters<LivechatRoomsRaw['updateOne']>, { bypassUnits?: boolean }?]) {
const [query, update, opts, extraOpts] = args;
if (extraOpts?.bypassUnits) {
// When calling updateOne from a service, we cannot call the meteor code inside the query restrictions
// So the solution now is to pass a bypassUnits flag to the updateOne method which prevents checking
// units restrictions on the query, but just for the query the service is actually using
// We need to find a way of remove the meteor dependency when fetching units, and then, we can remove this flag
return super.updateOne(query, update, opts);
}
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
return super.updateOne(restrictedQuery, update, opts);
}

async updateMany(...args: Parameters<LivechatRoomsRaw['updateMany']>) {
const [query, ...restArgs] = args;
const restrictedQuery = await addQueryRestrictionsToRoomsModel(query);
return super.updateMany(restrictedQuery, ...restArgs);
}

getConversationsBySource(start: Date, end: Date, extraQuery: Filter<IOmnichannelRoom>): AggregationCursor<ReportResult> {
return this.col.aggregate(
[
Expand Down
9 changes: 0 additions & 9 deletions apps/meteor/ee/server/models/raw/LivechatUnit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,6 @@ export class LivechatUnitRaw extends BaseRaw<IOmnichannelBusinessUnit> implement
return this.col.findOne(query, options);
}

async update(
originalQuery: Filter<IOmnichannelBusinessUnit>,
update: Filter<IOmnichannelBusinessUnit>,
options: FindOptions<IOmnichannelBusinessUnit>,
): Promise<UpdateResult> {
const query = await addQueryRestrictions(originalQuery);
return this.col.updateOne(query, update, options);
}

remove(query: Filter<IOmnichannelBusinessUnit>): Promise<DeleteResult> {
return this.deleteMany(query);
}
Expand Down
15 changes: 0 additions & 15 deletions apps/meteor/server/models/raw/LivechatRooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1518,11 +1518,6 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
{
$set: { pdfTranscriptRequested: true },
},
{},
// @ts-expect-error - extra arg not on base types
{
bypassUnits: true,
},
);
}

Expand All @@ -1534,11 +1529,6 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
{
$unset: { pdfTranscriptRequested: 1 },
},
{},
// @ts-expect-error - extra arg not on base types
{
bypassUnits: true,
},
);
}

Expand All @@ -1550,11 +1540,6 @@ export class LivechatRoomsRaw extends BaseRaw<IOmnichannelRoom> implements ILive
{
$set: { pdfTranscriptFileId: fileId },
},
{},
// @ts-expect-error - extra arg not on base types
{
bypassUnits: true,
},
);
}

Expand Down