From ab74eb3e5e144ac0a39931c7cf7c21e147fa9c2a Mon Sep 17 00:00:00 2001 From: Pierre Date: Tue, 30 Oct 2018 12:10:12 -0300 Subject: [PATCH 1/2] Added option to reset e2e key --- .../server/startup.js | 1 + .../rocketchat-e2e/server/models/Users.js | 8 ++++++++ packages/rocketchat-i18n/i18n/en.i18n.json | 3 +++ .../client/tabs/userActions.js | 15 ++++++++++++++ server/methods/resetUserE2EKey.js | 20 +++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 server/methods/resetUserE2EKey.js diff --git a/packages/rocketchat-authorization/server/startup.js b/packages/rocketchat-authorization/server/startup.js index 1ddb15068bad..27df066830e4 100644 --- a/packages/rocketchat-authorization/server/startup.js +++ b/packages/rocketchat-authorization/server/startup.js @@ -46,6 +46,7 @@ Meteor.startup(function() { { _id: 'mention-here', roles : ['admin', 'owner', 'moderator', 'user'] }, { _id: 'mute-user', roles : ['admin', 'owner', 'moderator'] }, { _id: 'remove-user', roles : ['admin', 'owner', 'moderator'] }, + { _id: 'reset-other-user-e2e-key', roles : ['admin'] }, { _id: 'run-import', roles : ['admin'] }, { _id: 'run-migration', roles : ['admin'] }, { _id: 'set-moderator', roles : ['admin', 'owner'] }, diff --git a/packages/rocketchat-e2e/server/models/Users.js b/packages/rocketchat-e2e/server/models/Users.js index ac072f960d45..3a099808b0d9 100644 --- a/packages/rocketchat-e2e/server/models/Users.js +++ b/packages/rocketchat-e2e/server/models/Users.js @@ -34,3 +34,11 @@ RocketChat.models.Users.findByIdsWithPublicE2EKey = function(ids, options) { return this.find(query, options); }; + +RocketChat.models.Users.resetE2EKey = function(userId) { + this.update({ _id: userId }, { + $unset: { + e2e: '', + }, + }); +}; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 85c2f12f82e4..f773dabd4702 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2200,6 +2200,8 @@ "Require_password_change": "Require password change", "Resend_verification_email": "Resend verification email", "Reset": "Reset", + "Reset_E2E_Key": "Reset E2E Key", + "reset-other-user-e2e-key": "Reset Other User E2E Key", "Reset_password": "Reset password", "Reset_section_settings": "Reset Section Settings", "Reset_Connection": "Reset Connection", @@ -2712,6 +2714,7 @@ "User_and_group_mentions_only": "User and group mentions only", "User_default": "User default", "User_doesnt_exist": "No user exists by the name of `@%s`.", + "User_e2e_key_was_reset": "User E2E key was reset successfully.", "User_has_been_activated": "User has been activated", "User_has_been_deactivated": "User has been deactivated", "User_has_been_deleted": "User has been deleted", diff --git a/packages/rocketchat-ui-flextab/client/tabs/userActions.js b/packages/rocketchat-ui-flextab/client/tabs/userActions.js index 24790cb1425b..7ceadd02af8e 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/userActions.js +++ b/packages/rocketchat-ui-flextab/client/tabs/userActions.js @@ -477,6 +477,21 @@ export const getActions = function({ user, directActions, hideAdminControls }) { name: t('Activate'), action: prevent(getUser, ({ _id }) => Meteor.call('setUserActiveStatus', _id, true, success(() => toastr.success(t('User_has_been_activated'))))), }; + }, () => { + if (hideAdminControls || !hasPermission('reset-other-user-e2e-key')) { + return; + } + if (!RocketChat.settings.get('E2E_Enable')) { + return; + } + + return { + group: 'admin', + icon: 'key', + id: 'reset-e2e', + name: t('Reset_E2E_Key'), + action: prevent(getUser, ({ _id }) => Meteor.call('resetUserE2EKey', _id, success(() => toastr.success(t('User_e2e_key_was_reset'))))), + }; }]; return actions; }; diff --git a/server/methods/resetUserE2EKey.js b/server/methods/resetUserE2EKey.js new file mode 100644 index 000000000000..f11c5ef97abb --- /dev/null +++ b/server/methods/resetUserE2EKey.js @@ -0,0 +1,20 @@ +Meteor.methods({ + resetUserE2EKey(userId) { + check(userId, String); + + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'resetUserE2EKey', + }); + } + + if (RocketChat.authz.hasPermission(Meteor.userId(), 'reset-other-user-e2e-key') !== true) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { + method: 'resetUserE2EKey', + }); + } + + RocketChat.models.Users.resetE2EKey(userId); + return true; + }, +}); From 50cc930e2688e18f2b3725c152f8901122990ddb Mon Sep 17 00:00:00 2001 From: Pierre Date: Tue, 6 Nov 2018 10:15:29 -0200 Subject: [PATCH 2/2] Reset e2e key --- .../rocketchat-e2e/client/rocketchat.e2e.js | 21 ++++++++++++ packages/rocketchat-e2e/server/index.js | 2 ++ .../server/methods/requestSubscriptionKeys.js | 33 +++++++++++++++++++ .../server}/methods/resetUserE2EKey.js | 11 +++++-- .../server/models/Subscriptions.js | 10 ++++++ .../rocketchat-lib/server/models/Users.js | 10 ++++++ .../client/tabs/userActions.js | 2 +- .../rocketchat-ui/client/lib/RoomManager.js | 10 ++++-- 8 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js rename {server => packages/rocketchat-e2e/server}/methods/resetUserE2EKey.js (57%) diff --git a/packages/rocketchat-e2e/client/rocketchat.e2e.js b/packages/rocketchat-e2e/client/rocketchat.e2e.js index 8a401a6b8f2c..2aec83f981fe 100644 --- a/packages/rocketchat-e2e/client/rocketchat.e2e.js +++ b/packages/rocketchat-e2e/client/rocketchat.e2e.js @@ -67,6 +67,10 @@ class E2E { _id: roomId, }); + if (!room) { + return; + } + if (room.encrypted !== true) { return; } @@ -185,6 +189,7 @@ class E2E { } async stopClient() { + console.log('E2E -> Stop Client'); // This flag is used to avoid closing unrelated alerts. if (showingE2EAlert) { alerts.close(); @@ -192,7 +197,16 @@ class E2E { localStorage.removeItem('public_key'); localStorage.removeItem('private_key'); + this.instancesByRoomId = {}; + this.privateKey = null; + this.enabled.set(false); + this._ready.set(false); this.started = false; + + this.readyPromise = new Deferred(); + this.readyPromise.then(() => { + this._ready.set(true); + }); } setupListeners() { @@ -236,6 +250,7 @@ class E2E { async loadKeysFromDB() { try { const { public_key, private_key } = await call('e2e.fetchMyKeys'); + this.db_public_key = public_key; this.db_private_key = private_key; } catch (error) { @@ -280,6 +295,12 @@ class E2E { } catch (error) { return console.error('E2E -> Error exporting private key: ', error); } + + this.requestSubscriptionKeys(); + } + + async requestSubscriptionKeys() { + call('e2e.requestSubscriptionKeys'); } createRandomPassword() { diff --git a/packages/rocketchat-e2e/server/index.js b/packages/rocketchat-e2e/server/index.js index 9380368df0e2..c7167c3f6fd3 100644 --- a/packages/rocketchat-e2e/server/index.js +++ b/packages/rocketchat-e2e/server/index.js @@ -9,6 +9,8 @@ import './methods/getUsersOfRoomWithoutKey'; import './methods/updateGroupKey'; import './methods/setRoomKeyID'; import './methods/fetchMyKeys'; +import './methods/resetUserE2EKey'; +import './methods/requestSubscriptionKeys'; RocketChat.callbacks.add('afterJoinRoom', (user, room) => { RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); diff --git a/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js b/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js new file mode 100644 index 000000000000..8653937d5b72 --- /dev/null +++ b/packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js @@ -0,0 +1,33 @@ +import { Meteor } from 'meteor/meteor'; +import { RocketChat } from 'meteor/rocketchat:lib'; + +Meteor.methods({ + 'e2e.requestSubscriptionKeys'() { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { + method: 'requestSubscriptionKeys', + }); + } + + // Get all encrypted rooms that the user is subscribed to + + const subscriptions = RocketChat.models.Subscriptions.findByUserId(Meteor.userId()); + const roomIds = subscriptions.map((subscription) => subscription.rid); + + const query = { + e2eKeyId : { + $exists: true, + }, + _id : { + $in: roomIds, + }, + }; + + const rooms = RocketChat.models.Rooms.find(query); + rooms.forEach((room) => { + RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId); + }); + + return true; + }, +}); diff --git a/server/methods/resetUserE2EKey.js b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js similarity index 57% rename from server/methods/resetUserE2EKey.js rename to packages/rocketchat-e2e/server/methods/resetUserE2EKey.js index f11c5ef97abb..53599f16d284 100644 --- a/server/methods/resetUserE2EKey.js +++ b/packages/rocketchat-e2e/server/methods/resetUserE2EKey.js @@ -1,7 +1,8 @@ -Meteor.methods({ - resetUserE2EKey(userId) { - check(userId, String); +import { Meteor } from 'meteor/meteor'; +import { RocketChat } from 'meteor/rocketchat:lib'; +Meteor.methods({ + 'e2e.resetUserE2EKey'(userId) { if (!Meteor.userId()) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'resetUserE2EKey', @@ -15,6 +16,10 @@ Meteor.methods({ } RocketChat.models.Users.resetE2EKey(userId); + RocketChat.models.Subscriptions.resetUserE2EKey(userId); + + // Force the user to logout, so that the keys can be generated again + RocketChat.models.Users.removeResumeService(userId); return true; }, }); diff --git a/packages/rocketchat-e2e/server/models/Subscriptions.js b/packages/rocketchat-e2e/server/models/Subscriptions.js index 88af4c7d357b..e949c1929917 100644 --- a/packages/rocketchat-e2e/server/models/Subscriptions.js +++ b/packages/rocketchat-e2e/server/models/Subscriptions.js @@ -17,3 +17,13 @@ RocketChat.models.Subscriptions.findByRidWithoutE2EKey = function(rid, options) return this.find(query, options); }; + +RocketChat.models.Subscriptions.resetUserE2EKey = function(userId) { + this.update({ 'u._id': userId }, { + $unset: { + E2EKey: '', + }, + }, { + multi: true, + }); +}; diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js index 70381872451c..f56f1a5617b5 100644 --- a/packages/rocketchat-lib/server/models/Users.js +++ b/packages/rocketchat-lib/server/models/Users.js @@ -602,6 +602,16 @@ class ModelUsers extends RocketChat.models._Base { return this.update({ _id }, update); } + removeResumeService(_id) { + const update = { + $unset: { + 'services.resume': '', + }, + }; + + return this.update({ _id }, update); + } + // INSERT create(data) { const user = { diff --git a/packages/rocketchat-ui-flextab/client/tabs/userActions.js b/packages/rocketchat-ui-flextab/client/tabs/userActions.js index 7ceadd02af8e..7e11f39d23d8 100644 --- a/packages/rocketchat-ui-flextab/client/tabs/userActions.js +++ b/packages/rocketchat-ui-flextab/client/tabs/userActions.js @@ -490,7 +490,7 @@ export const getActions = function({ user, directActions, hideAdminControls }) { icon: 'key', id: 'reset-e2e', name: t('Reset_E2E_Key'), - action: prevent(getUser, ({ _id }) => Meteor.call('resetUserE2EKey', _id, success(() => toastr.success(t('User_e2e_key_was_reset'))))), + action: prevent(getUser, ({ _id }) => Meteor.call('e2e.resetUserE2EKey', _id, success(() => toastr.success(t('User_e2e_key_was_reset'))))), }; }]; return actions; diff --git a/packages/rocketchat-ui/client/lib/RoomManager.js b/packages/rocketchat-ui/client/lib/RoomManager.js index 82904ec43bb5..3dded93d3a17 100644 --- a/packages/rocketchat-ui/client/lib/RoomManager.js +++ b/packages/rocketchat-ui/client/lib/RoomManager.js @@ -236,9 +236,13 @@ const loadMissedMessages = function(rid) { return; } const subscription = ChatSubscription.findOne({ rid }); - return Meteor.call('loadMissedMessages', rid, lastMessage.ts, (err, result) => - Array.from(result).map((item) => RocketChat.promises.run('onClientMessageReceived', item).then((msg) => upsertMessage({ msg, subscription }))) - ); + return Meteor.call('loadMissedMessages', rid, lastMessage.ts, (err, result) => { + if (result) { + return Array.from(result).map((item) => RocketChat.promises.run('onClientMessageReceived', item).then((msg) => upsertMessage({ msg, subscription }))); + } else { + return []; + } + }); }; let connectionWasOnline = true;