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
3 changes: 3 additions & 0 deletions app/livechat/imports/server/rest/upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ API.v1.addRoute('livechat/upload/:rid', {
};

const uploadedFile = Meteor.wrapAsync(fileStore.insert.bind(fileStore))(details, file.fileBuffer);
if (!uploadedFile) {
return API.v1.error('Invalid file');
}

uploadedFile.description = fields.description;

Expand Down
11 changes: 10 additions & 1 deletion app/livechat/server/roomAccessValidator.compatibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@ import { RoutingManager } from './lib/RoutingManager';

export const validators = [
function(room, user) {
if (!user?._id) {
return false;
}
return hasPermission(user._id, 'view-livechat-rooms');
},
function(room, user) {
if (!user?._id) {
return false;
}
const { _id: userId } = user;
const { servedBy: { _id: agentId } = {} } = room;
return userId === agentId || (!room.open && hasPermission(user._id, 'view-livechat-room-closed-by-another-agent'));
Expand All @@ -19,6 +25,9 @@ export const validators = [
return extraData && extraData.visitorToken && room.v && room.v.token === extraData.visitorToken;
},
function(room, user) {
if (!user?._id) {
return false;
}
const { previewRoom } = RoutingManager.getConfig();
if (!previewRoom) {
return;
Expand All @@ -39,7 +48,7 @@ export const validators = [
return inquiry && inquiry.status === 'queued';
},
function(room, user) {
if (!room.departmentId || room.open) {
if (!room.departmentId || room.open || !user?._id) {
return;
}
const agentOfDepartment = LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId);
Expand Down
2 changes: 1 addition & 1 deletion app/livechat/server/roomAccessValidator.internalService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class AuthorizationLivechat extends ServiceClass implements IAuthorizationLivech

protected internal = true;

async canAccessRoom(room: Partial<IRoom>, user: Pick<IUser, '_id'>, extraData?: object): Promise<boolean> {
async canAccessRoom(room: Partial<IRoom>, user: Partial<IUser>, extraData?: object): Promise<boolean> {
for (const validator of validators) {
if (validator(room, user, extraData)) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class AuthorizationTokenpass extends ServiceClass implements IAuthorizationToken

protected internal = true;

async canAccessRoom(room: Partial<IRoom>, user: Pick<IUser, '_id'>): Promise<boolean> {
async canAccessRoom(room: Partial<IRoom>, user: Partial<IUser>): Promise<boolean> {
for (const validator of validators) {
if (validator(room, user)) {
return true;
Expand Down
26 changes: 11 additions & 15 deletions server/modules/notifications/notifications.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,16 @@ export class NotificationsModule {
const notifyUser = this.notifyUser.bind(this);

this.streamRoomMessage.allowWrite('none');
this.streamRoomMessage.allowRead(async function(eventName /* , args*/) {
if (!this.userId) {
return false;
}

this.streamRoomMessage.allowRead(async function(eventName, extraData) {
const room = await Rooms.findOneById(eventName);
if (!room) {
return false;
}

const canAccess = await Authorization.canAccessRoom(room, { _id: this.userId });
const canAccess = await Authorization.canAccessRoom(room, { _id: this.userId || '' }, extraData);
if (!canAccess) {
// verify if can preview messages from public channels
if (room.t === 'c') {
if (room.t === 'c' && this.userId) {
return Authorization.hasPermission(this.userId, 'preview-c-room');
}
return false;
Expand Down Expand Up @@ -159,10 +155,6 @@ export class NotificationsModule {
this.streamLogged.allowRead('logged');

this.streamRoom.allowRead(async function(eventName, extraData) {
if (!this.userId) {
return false;
}

const [rid] = eventName.split('/');

// typing from livechat widget
Expand All @@ -172,6 +164,10 @@ export class NotificationsModule {
return room && room.t === 'l' && room.v.token === extraData.token;
}

if (!this.userId) {
return false;
}

const subsCount = await Subscriptions.countByRoomIdAndUserId(rid, this.userId);
return subsCount > 0;
});
Expand All @@ -188,10 +184,6 @@ export class NotificationsModule {
return false;
}

if (!this.userId) {
return false;
}

try {
// TODO consider using something to cache settings
const key = await Settings.getValueById('UI_Use_Real_Name') ? 'name' : 'username';
Expand All @@ -203,6 +195,10 @@ export class NotificationsModule {
return room && room.t === 'l' && room.v.token === extraData.token;
}

if (!this.userId) {
return false;
}

const user = await Users.findOneById(this.userId, {
projection: {
[key]: 1,
Expand Down
2 changes: 1 addition & 1 deletion server/sdk/types/IAuthorization.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IRoom } from '../../../definition/IRoom';
import { IUser } from '../../../definition/IUser';

export type RoomAccessValidator = (room: Partial<IRoom>, user: Pick<IUser, '_id'>, extraData?: object) => Promise<boolean>;
export type RoomAccessValidator = (room: Partial<IRoom>, user: Partial<IUser>, extraData?: Record<string, any>) => Promise<boolean>;

export interface IAuthorization {
hasAllPermission(userId: string, permissions: string[], scope?: string): Promise<boolean>;
Expand Down
33 changes: 17 additions & 16 deletions server/services/authorization/canAccessRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ import { Subscriptions, Rooms, Settings } from './service';

const roomAccessValidators: RoomAccessValidator[] = [
async function(room, user): Promise<boolean> {
if (room.t === 'c') {
// TODO: it was using cached version from /app/settings/server/raw.js
const anonymous = await Settings.getValueById('Accounts_AllowAnonymousRead');
if (!user._id && anonymous === true) {
return true;
}
if (!room?._id || room.t !== 'c') {
return false;
}

return Authorization.hasPermission(user._id, 'view-c-room');
if (!user?._id) {
// TODO: it was using cached version from /app/settings/server/raw.js
const anon = await Settings.getValueById('Accounts_AllowAnonymousRead');
return !!anon;
}

return false;
return Authorization.hasPermission(user._id, 'view-c-room');
},

async function(room, user): Promise<boolean> {
if (!room._id) {
if (!room?._id || !user?._id) {
return false;
}
if (await Subscriptions.countByRoomIdAndUserId(room._id, user._id)) {
Expand All @@ -31,23 +31,24 @@ const roomAccessValidators: RoomAccessValidator[] = [
},

async function(room, user): Promise<boolean> {
if (!room.prid) {
if (!room?.prid) {
return false;
}
room = await Rooms.findOne(room.prid);
if (!room) {
const parentRoom = await Rooms.findOne(room.prid);
if (!parentRoom) {
return false;
}
return Authorization.canAccessRoom(room, user);
return Authorization.canAccessRoom(parentRoom, user);
},
canAccessRoomLivechat,
canAccessRoomTokenpass,
];

export const canAccessRoom: RoomAccessValidator = async (room, user, extraData): Promise<boolean> => {
if (!room || !user) {
return false;
}
// TODO livechat can send both as null, so they we need to validate nevertheless
// if (!room || !user) {
// return false;
// }

for await (const roomAccessValidator of roomAccessValidators) {
if (await roomAccessValidator(room, user, extraData)) {
Expand Down
10 changes: 7 additions & 3 deletions server/services/authorization/canAccessRoomLivechat.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { IAuthorizationLivechat } from '../../sdk/types/IAuthorizationLivechat';
import { proxifyWithWait } from '../../sdk/lib/proxify';
import { RoomAccessValidator } from '../../sdk/types/IAuthorization';
import { Rooms } from './service';

export const AuthorizationLivechat = proxifyWithWait<IAuthorizationLivechat>('authorization-livechat');

export const canAccessRoomLivechat: RoomAccessValidator = async (room, user): Promise<boolean> => {
if (room.t !== 'l') {
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 (livechatRoom?.t !== 'l') {
return false;
}

// Call back core temporarily
return AuthorizationLivechat.canAccessRoom(room, user);
return AuthorizationLivechat.canAccessRoom(livechatRoom, user, extraData);
};
2 changes: 1 addition & 1 deletion server/services/authorization/canAccessRoomTokenpass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { RoomAccessValidator } from '../../sdk/types/IAuthorization';
export const AuthorizationTokenpass = proxifyWithWait<IAuthorizationTokenpass>('authorization-tokenpass');

export const canAccessRoomTokenpass: RoomAccessValidator = async (room, user): Promise<boolean> => {
if (!room.tokenpass) {
if (!room?.tokenpass) {
return false;
}

Expand Down