Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] Ensure E2E is enabled/disabled on sending message #21084

Merged
merged 5 commits into from
Mar 23, 2021
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
12 changes: 12 additions & 0 deletions app/e2e/client/E2ERoomState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum E2ERoomState {
NO_PASSWORD_SET = 'NO_PASSWORD_SET',
NOT_STARTED = 'NOT_STARTED',
DISABLED = 'DISABLED',
HANDSHAKE = 'HANDSHAKE',
ESTABLISHING = 'ESTABLISHING',
CREATING_KEYS = 'CREATING_KEYS',
WAITING_KEYS = 'WAITING_KEYS',
KEYS_RECEIVED = 'KEYS_RECEIVED',
READY = 'READY',
ERROR = 'ERROR',
}
19 changes: 19 additions & 0 deletions app/e2e/client/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getConfig } from '../../ui-utils/client/config';

let debug: boolean | undefined = undefined;

const isDebugEnabled = (): boolean => {
if (debug === undefined) {
debug = getConfig('debug') === 'true' || getConfig('debug-e2e') === 'true';
}

return debug;
};

export const log = (context: string, ...msg: unknown[]): void => {
isDebugEnabled() && console.log(`[${ context }]`, ...msg);
};

export const logError = (context: string, ...msg: unknown[]): void => {
isDebugEnabled() && console.error(`[${ context }]`, ...msg);
};
156 changes: 18 additions & 138 deletions app/e2e/client/rocketchat.e2e.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { EJSON } from 'meteor/ejson';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { Emitter } from '@rocket.chat/emitter';

Expand All @@ -23,24 +21,14 @@ import {
} from './helper';
import * as banners from '../../../client/lib/banners';
import { Rooms, Subscriptions, Messages } from '../../models';
import { promises } from '../../promises/client';
import { settings } from '../../settings';
import { Notifications } from '../../notifications/client';
import { Layout, call, modal } from '../../ui-utils';
import { call, modal } from '../../ui-utils';
import './events.js';
import './tabbar';
import { getConfig } from '../../ui-utils/client/config';

const debug = [getConfig('debug'), getConfig('debug-e2e')].includes('true');
import { log, logError } from './logger';
import { waitUntilFind } from './waitUntilFind';

let failedToDecodeKey = false;

const waitUntilFind = (fn) => new Promise((resolve) => {
Tracker.autorun((c) => {
const result = fn();
return result && resolve(result) && c.stop();
});
});
class E2E extends Emitter {
constructor() {
super();
Expand All @@ -60,14 +48,13 @@ class E2E extends Emitter {
}

log(...msg) {
debug && console.log('[E2E]', ...msg);
log('E2E', ...msg);
}

error(...msg) {
debug && console.error('[E2E]', ...msg);
logError('E2E', ...msg);
}


isEnabled() {
return this.enabled.get();
}
Expand All @@ -76,30 +63,26 @@ class E2E extends Emitter {
return this.enabled.get() && this._ready.get();
}

getE2ERoom(rid) {
return this.instancesByRoomId[rid];
}

removeInstanceByRoomId(rid) {
delete this.instancesByRoomId[rid];
}

async getInstanceByRoomId(roomId) {
const room = await waitUntilFind(() => Rooms.findOne({
_id: roomId,
}));
async getInstanceByRoomId(rid) {
const room = await waitUntilFind(() => Rooms.findOne({ _id: rid }));

if (room.t !== 'd' && room.t !== 'p') {
return;
return null;
}

if (room.encrypted !== true && !room.e2eKeyId) {
return;
return null;
}

this.instancesByRoomId[roomId] = this.instancesByRoomId[roomId] ?? new E2ERoom(Meteor.userId(), roomId, room.t);
if (!this.instancesByRoomId[rid]) {
this.instancesByRoomId[rid] = new E2ERoom(Meteor.userId(), rid, room.t);
}

return this.instancesByRoomId[roomId];
return this.instancesByRoomId[rid];
}

removeInstanceByRoomId(rid) {
delete this.instancesByRoomId[rid];
}

async startClient() {
Expand Down Expand Up @@ -376,7 +359,7 @@ class E2E extends Emitter {
return message;
}

const e2eRoom = this.getE2ERoom(message.rid);
const e2eRoom = await this.getInstanceByRoomId(message.rid);

if (!e2eRoom) {
return message;
Expand Down Expand Up @@ -423,106 +406,3 @@ class E2E extends Emitter {
}

export const e2e = new E2E();

const handle = async (roomId, keyId) => {
const e2eRoom = await e2e.getInstanceByRoomId(roomId);
if (!e2eRoom) {
return;
}

e2eRoom.provideKeyToUser(keyId);
};

Meteor.startup(function() {
Tracker.autorun(function() {
if (Meteor.userId()) {
const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin');

if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) {
e2e.startClient();
e2e.enabled.set(true);
} else {
e2e.enabled.set(false);
e2e.closeAlert();
}
}
});

let observable = null;
Tracker.autorun(() => {
if (!e2e.isReady()) {
promises.remove('onClientMessageReceived', 'e2e-decript-message');
Notifications.unUser('e2ekeyRequest', handle);
observable?.stop();
return promises.remove('onClientBeforeSendMessage', 'e2e');
}


Notifications.onUser('e2ekeyRequest', handle);


observable = Subscriptions.find().observe({
changed: async (doc) => {
if (!doc.encrypted && !doc.E2EKey) {
return e2e.removeInstanceByRoomId(doc.rid);
}
const e2eRoom = await e2e.getInstanceByRoomId(doc.rid);

if (!e2eRoom) {
return;
}


doc.encrypted ? e2eRoom.unPause() : e2eRoom.pause();

// Cover private groups and direct messages
if (!e2eRoom.isSupportedRoomType(doc.t)) {
return e2eRoom.disable();
}


if (doc.E2EKey && e2eRoom.isWaitingKeys()) {
return e2eRoom.keyReceived();
}
if (!e2eRoom.isReady()) {
return;
}
e2eRoom.decryptSubscription();
},
added: async (doc) => {
if (!doc.encrypted && !doc.E2EKey) {
return;
}
return e2e.getInstanceByRoomId(doc.rid);
},
removed: (doc) => {
e2e.removeInstanceByRoomId(doc.rid);
},
});

promises.add('onClientMessageReceived', (msg) => {
const e2eRoom = e2e.getE2ERoom(msg.rid);
if (!e2eRoom || !e2eRoom.shouldConvertReceivedMessages()) {
return msg;
}
return e2e.decryptMessage(msg);
}, promises.priority.HIGH, 'e2e-decript-message');

// Encrypt messages before sending
promises.add('onClientBeforeSendMessage', async function(message) {
const e2eRoom = e2e.getE2ERoom(message.rid);
if (!e2eRoom || !e2eRoom.shouldConvertSentMessages()) {
return message;
}
// Should encrypt this message.
return e2eRoom
.encrypt(message)
.then((msg) => {
message.msg = msg;
message.t = 'e2e';
message.e2e = 'pending';
return message;
});
}, promises.priority.HIGH, 'e2e');
});
});
Loading