Regression: Fix livechat permission validations (#19468)

pull/19460/head^2
Diego Sampaio 6 years ago committed by GitHub
parent 65fabc1fc7
commit e7b4fef50d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      app/livechat/imports/server/rest/upload.js
  2. 11
      app/livechat/server/roomAccessValidator.compatibility.js
  3. 2
      app/livechat/server/roomAccessValidator.internalService.ts
  4. 2
      app/tokenpass/server/roomAccessValidator.internalService.ts
  5. 26
      server/modules/notifications/notifications.module.ts
  6. 2
      server/sdk/types/IAuthorization.ts
  7. 33
      server/services/authorization/canAccessRoom.ts
  8. 10
      server/services/authorization/canAccessRoomLivechat.ts
  9. 2
      server/services/authorization/canAccessRoomTokenpass.ts

@ -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;

@ -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'));
@ -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;
@ -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);

@ -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;

@ -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;

@ -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;
@ -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
@ -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;
});
@ -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';
@ -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,

@ -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>;

@ -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)) {
@ -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)) {

@ -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);
};

@ -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;
}

Loading…
Cancel
Save