From 7acbb2a320bbf8c9b11bc9773e9277a133d08d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Albuquerque?= Date: Tue, 6 Sep 2022 16:09:56 -0300 Subject: [PATCH] [IMPROVE] OTR Message (#24297) Co-authored-by: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> --- .../app/lib/server/functions/sendMessage.js | 6 +++- apps/meteor/app/otr/client/OTRRoom.ts | 24 ++++++------- apps/meteor/app/otr/lib/IOTR.ts | 2 +- apps/meteor/app/otr/server/index.ts | 2 +- .../app/otr/server/methods/updateOTRAck.ts | 8 ++--- .../app/ui-utils/client/lib/RoomManager.ts | 2 +- apps/meteor/client/startup/otr.ts | 34 ++++++++++++------- .../notifications/notifications.module.ts | 10 +++++- .../core-typings/src/IMessage/IMessage.ts | 3 +- 9 files changed, 55 insertions(+), 36 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/sendMessage.js b/apps/meteor/app/lib/server/functions/sendMessage.js index e80b527573f..f067a3ad78a 100644 --- a/apps/meteor/app/lib/server/functions/sendMessage.js +++ b/apps/meteor/app/lib/server/functions/sendMessage.js @@ -10,6 +10,7 @@ import { hasPermission } from '../../../authorization/server'; import { SystemLogger } from '../../../../server/lib/logger/system'; import { parseUrlsInMessage } from './parseUrlsInMessage'; import { isRelativeURL } from '../../../../lib/utils/isRelativeURL'; +import notifications from '../../../notifications/server/lib/Notifications'; /** * IMPORTANT @@ -244,7 +245,10 @@ export const sendMessage = function (user, message, room, upsert = false) { message = callbacks.run('beforeSaveMessage', message, room); if (message) { - if (message._id && upsert) { + if (message.t === 'otr') { + const otrStreamer = notifications.streamRoomMessage; + otrStreamer.emit(message.rid, message, user, room); + } else if (message._id && upsert) { const { _id } = message; delete message._id; Messages.upsert( diff --git a/apps/meteor/app/otr/client/OTRRoom.ts b/apps/meteor/app/otr/client/OTRRoom.ts index f722a6191cc..57c4097c844 100644 --- a/apps/meteor/app/otr/client/OTRRoom.ts +++ b/apps/meteor/app/otr/client/OTRRoom.ts @@ -66,8 +66,7 @@ export class OTRRoom implements IOTRRoom { } setState(nextState: OtrRoomState): void { - const currentState = this.state.get(); - if (currentState === nextState) { + if (this.getState() === nextState) { return; } @@ -141,7 +140,7 @@ export class OTRRoom implements IOTRRoom { this._userOnlineComputation = Tracker.autorun(() => { const $room = $(`#chat-window-${this._roomId}`); const $title = $('.rc-header__title', $room); - if (this.state.get() === OtrRoomState.ESTABLISHED) { + if (this.getState() === OtrRoomState.ESTABLISHED) { if ($room.length && $title.length && !$('.otr-icon', $title).length) { $title.prepend(""); } @@ -252,8 +251,8 @@ export class OTRRoom implements IOTRRoom { const establishConnection = async (): Promise => { this.setState(OtrRoomState.ESTABLISHING); Meteor.clearTimeout(timeout); - try { + if (!data.publicKey) throw new Error('Public key is not generated'); await this.generateKeyPair(); await this.importPublicKey(data.publicKey); await goToRoomById(data.roomId); @@ -283,11 +282,11 @@ export class OTRRoom implements IOTRRoom { throw new Meteor.Error('user-not-defined', 'User not defined.'); } - if (data.refresh && this.state.get() === OtrRoomState.ESTABLISHED) { + if (data.refresh && this.getState() === OtrRoomState.ESTABLISHED) { this.reset(); await establishConnection(); } else { - if (this.state.get() === OtrRoomState.ESTABLISHED) { + if (this.getState() === OtrRoomState.ESTABLISHED) { this.reset(); } imperativeModal.open({ @@ -308,11 +307,11 @@ export class OTRRoom implements IOTRRoom { }, }, }); + timeout = Meteor.setTimeout(() => { + this.setState(OtrRoomState.TIMEOUT); + imperativeModal.close(); + }, 10000); } - timeout = Meteor.setTimeout(() => { - this.setState(OtrRoomState.TIMEOUT); - imperativeModal.close(); - }, 10000); } catch (e) { dispatchToastMessage({ type: 'error', message: e }); } @@ -320,6 +319,7 @@ export class OTRRoom implements IOTRRoom { case 'acknowledge': try { + if (!data.publicKey) throw new Error('Public key is not generated'); await this.importPublicKey(data.publicKey); this.setState(OtrRoomState.ESTABLISHED); @@ -334,7 +334,7 @@ export class OTRRoom implements IOTRRoom { break; case 'deny': - if (this.state.get() === OtrRoomState.ESTABLISHING) { + if (this.getState() === OtrRoomState.ESTABLISHING) { this.reset(); this.setState(OtrRoomState.DECLINED); } @@ -347,7 +347,7 @@ export class OTRRoom implements IOTRRoom { throw new Meteor.Error('user-not-defined', 'User not defined.'); } - if (this.state.get() === OtrRoomState.ESTABLISHED) { + if (this.getState() === OtrRoomState.ESTABLISHED) { this.reset(); this.setState(OtrRoomState.NOT_STARTED); imperativeModal.open({ diff --git a/apps/meteor/app/otr/lib/IOTR.ts b/apps/meteor/app/otr/lib/IOTR.ts index 33548be6a43..da595114a9d 100644 --- a/apps/meteor/app/otr/lib/IOTR.ts +++ b/apps/meteor/app/otr/lib/IOTR.ts @@ -5,8 +5,8 @@ import type { OTRRoom } from '../client/OTRRoom'; export interface IOnUserStreamData { roomId: IRoom['_id']; - publicKey: string; userId: IUser['_id']; + publicKey?: string; refresh?: boolean; } diff --git a/apps/meteor/app/otr/server/index.ts b/apps/meteor/app/otr/server/index.ts index 8d489ec9c16..3657e7d2393 100644 --- a/apps/meteor/app/otr/server/index.ts +++ b/apps/meteor/app/otr/server/index.ts @@ -1,4 +1,4 @@ import './settings'; -import './methods/deleteOldOTRMessages'; import './methods/updateOTRAck'; import './methods/sendSystemMessages'; +import './methods/deleteOldOTRMessages'; diff --git a/apps/meteor/app/otr/server/methods/updateOTRAck.ts b/apps/meteor/app/otr/server/methods/updateOTRAck.ts index b4444ac5656..1eff9d8638e 100644 --- a/apps/meteor/app/otr/server/methods/updateOTRAck.ts +++ b/apps/meteor/app/otr/server/methods/updateOTRAck.ts @@ -1,13 +1,13 @@ -import type { IMessage } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; -import { Messages } from '../../../models/server'; +import notifications from '../../../notifications/server/lib/Notifications'; Meteor.methods({ - updateOTRAck(_id: IMessage['_id'], ack: IMessage['otrAck']): void { + updateOTRAck({ message, ack }) { if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'updateOTRAck' }); } - Messages.updateOTRAck(_id, ack); + const otrStreamer = notifications.streamRoomMessage; + otrStreamer.emit(message.rid, { ...message, otr: { ack } }); }, }); diff --git a/apps/meteor/app/ui-utils/client/lib/RoomManager.ts b/apps/meteor/app/ui-utils/client/lib/RoomManager.ts index 85f281f9440..3ba6756212c 100644 --- a/apps/meteor/app/ui-utils/client/lib/RoomManager.ts +++ b/apps/meteor/app/ui-utils/client/lib/RoomManager.ts @@ -193,7 +193,7 @@ const computation = Tracker.autorun(() => { } } - msg.name = room.name; + msg.name = room.name || ''; handleTrackSettingsChange(msg); diff --git a/apps/meteor/client/startup/otr.ts b/apps/meteor/client/startup/otr.ts index f10d1a298dd..e63b76275d5 100644 --- a/apps/meteor/client/startup/otr.ts +++ b/apps/meteor/client/startup/otr.ts @@ -1,23 +1,32 @@ -import { AtLeast, IMessage } from '@rocket.chat/core-typings'; +import { IMessage, IRoom, IUser, AtLeast } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; import { Notifications } from '../../app/notifications/client'; import OTR from '../../app/otr/client/OTR'; -import { IOnUserStreamData, IOTRDecrypt } from '../../app/otr/lib/IOTR'; import { OtrRoomState } from '../../app/otr/lib/OtrRoomState'; import { t } from '../../app/utils/client'; import { onClientBeforeSendMessage } from '../lib/onClientBeforeSendMessage'; import { onClientMessageReceived } from '../lib/onClientMessageReceived'; +type NotifyUserData = { + roomId: IRoom['_id']; + userId: IUser['_id']; +}; + Meteor.startup(() => { Tracker.autorun(() => { if (Meteor.userId()) { - Notifications.onUser('otr', (type: string, data: IOnUserStreamData) => { + Notifications.onUser('otr', (type: string, data: NotifyUserData) => { + if (!data.roomId || !data.userId || data.userId === Meteor.userId()) { + return; + } const instanceByRoomId = OTR.getInstanceByRoomId(data.roomId); - if (!data.roomId || !data.userId || data.userId === Meteor.userId() || !instanceByRoomId) { + + if (!instanceByRoomId) { return; } + instanceByRoomId.onUserStream(type, data); }); } @@ -37,33 +46,32 @@ Meteor.startup(() => { const instanceByRoomId = OTR.getInstanceByRoomId(message.rid); if (message.rid && instanceByRoomId && instanceByRoomId.getState() === OtrRoomState.ESTABLISHED) { - if (message.notification) { + if (message?.notification) { message.msg = t('Encrypted_message'); return message; } - if (message?.t !== 'otr') { + if (message.t !== 'otr') { return message; } - const otrRoom = instanceByRoomId; - const decrypted = await otrRoom.decrypt(message.msg); + + const decrypted = await instanceByRoomId.decrypt(message.msg); if (typeof decrypted === 'string') { return { ...message, msg: decrypted }; } - const { _id, text: msg, ack, ts, userId }: IOTRDecrypt = decrypted; + const { _id, text: msg, ack, ts, userId } = decrypted; if (ts) message.ts = ts; if (message.otrAck) { - const otrAck = await otrRoom.decrypt(message.otrAck); + const otrAck = await instanceByRoomId.decrypt(message.otrAck); if (typeof otrAck === 'string') { return { ...message, msg: otrAck }; } - if (ack === otrAck.text) message.t = 'otr-ack'; } else if (userId !== Meteor.userId()) { - const encryptedAck = await otrRoom.encryptText(ack); + const encryptedAck = await instanceByRoomId.encryptText(ack); - Meteor.call('updateOTRAck', _id, encryptedAck); + Meteor.call('updateOTRAck', { message, ack: encryptedAck }); } return { ...message, _id, msg }; diff --git a/apps/meteor/server/modules/notifications/notifications.module.ts b/apps/meteor/server/modules/notifications/notifications.module.ts index 5da8d00d757..e2243c0fccb 100644 --- a/apps/meteor/server/modules/notifications/notifications.module.ts +++ b/apps/meteor/server/modules/notifications/notifications.module.ts @@ -282,6 +282,10 @@ export class NotificationsModule { this.streamUser.allowWrite(async function (eventName: string, data: unknown) { const [, e] = eventName.split('/'); + if (e === 'otr' && (data === 'handshake' || data === 'acknowledge')) { + const isEnable = await Settings.getValueById('OTR_Enable'); + return Boolean(this.userId) && (isEnable === 'true' || isEnable === true); + } if (e === 'webrtc') { return true; } @@ -308,11 +312,15 @@ export class NotificationsModule { this.streamUser.allowRead(async function (eventName) { const [userId, e] = eventName.split('/'); + if (e === 'otr') { + const isEnable = await Settings.getValueById('OTR_Enable'); + return Boolean(this.userId) && this.userId === userId && (isEnable === 'true' || isEnable === true); + } if (e === 'webrtc') { return true; } - return this.userId != null && this.userId === userId; + return Boolean(this.userId) && this.userId === userId; }); this.streamImporters.allowRead('all'); diff --git a/packages/core-typings/src/IMessage/IMessage.ts b/packages/core-typings/src/IMessage/IMessage.ts index 6b12e428b5d..44bc4f48600 100644 --- a/packages/core-typings/src/IMessage/IMessage.ts +++ b/packages/core-typings/src/IMessage/IMessage.ts @@ -57,12 +57,11 @@ type OmnichannelTypesValues = | 'omnichannel_on_hold_chat_resumed'; type OtrMessageTypeValues = 'otr' | 'otr-ack'; + type OtrSystemMessages = 'user_joined_otr' | 'user_requested_otr_key_refresh' | 'user_key_refreshed_successfully'; export type MessageTypesValues = | 'e2e' - | 'otr' - | 'otr-ack' | 'uj' | 'ul' | 'ru'