Remove directly dependency between lib and e2e (#13115)

* Move rocketchat settings to specific package

* WIP: Move models from rocketchat-lib to a specific package (server)

* Move function from rocketchat:lib to rocketchat:utils to use it in rocketchat:models

* Move client models from rocketchat:lib to rocketchat:models

* Fix lint

* Move rocketchat.info from lib to utils

* Remove directly dependency between lib and migrations

* Move statistics Model to rocketchat:models

* Create rocketchat:metrics to be able to depacking rocketchat callbacks

* Move  callbacks to specific package

* Remove unused dependency

* Move rocketchat-notifications to a specific package

* Move rocketchat-promises to a specific package

* remove directly dependency from metrics and models

* Move CachedCollection from lib to models

* Move ui models/collections from ui to models

* Move authorization client/ui models to rocketchat:models to be able to remove lib dependency

* Creation of rocketchat:ui-utils to help decouple rocketchat:lib and rocketchat:authz

* Move some common functions to rocketchat:utils

* Change imports to dynamic imports to avoid directly dependency between some packages

* Move authz models to rocketchat:models

* Remove directly dependency between rocketchat:authz and rocketchat:lib

* Move some functions from rocketchat:lib to rocketchat:utils

* Add functions to settings package

* Convert rocketchat:file-upload to main module structure

* Import FileUpload where it is being used

* Remove FileUpload and fileUploadHandler from globals eslintrc

* Move some  functions to rocketchat:ui-utils

* Remove directly dependency between rocketchat:authorization and rocketchat:ui-utils

* Remove dependency between lazy-load and lib

* Change imports of renderMessageBody from ui-message to ui-utils

* Add import of main ready from ui-utils

* Convert rocketchat-ui-sidenav to main module structure

* Add imports of toolbarSearch from ui-sidenav

* Remove toolbarSearch from eslintrc globals

* Move CachedCollection to a specific package

* Change imports of CachedCollection to new package

* Move some functions to rocketchat:ui-utils

* Remove directly dependency between tooltip and lib

*  Remove directly dependency between settings and metrics

* Move some settings client function from lib to settings

* Convert rocketchat-ui-master to main module structure

* Remove directly dependency between rocketchat:e2e and rocketchat:lib

* Fix wrong import and lint

* Merge branch 'develop' into globals/move-rocketchat-callbacks

* Fix import missed objects inside RocketChat namespace

* Fix lint
pull/13117/head^2
Marcos Spessatto Defendi 6 years ago committed by Rodrigo Nascimento
parent deae2a43dc
commit e8f05c2702
  1. 4
      packages/rocketchat-e2e/client/accountEncryption.js
  2. 37
      packages/rocketchat-e2e/client/rocketchat.e2e.js
  3. 13
      packages/rocketchat-e2e/client/rocketchat.e2e.room.js
  4. 8
      packages/rocketchat-e2e/package.js
  5. 10
      packages/rocketchat-e2e/server/index.js
  6. 4
      packages/rocketchat-e2e/server/methods/fetchMyKeys.js
  7. 6
      packages/rocketchat-e2e/server/methods/getUsersOfRoomWithoutKey.js
  8. 9
      packages/rocketchat-e2e/server/methods/requestSubscriptionKeys.js
  9. 11
      packages/rocketchat-e2e/server/methods/resetUserE2EKey.js
  10. 4
      packages/rocketchat-e2e/server/methods/setRoomKeyID.js
  11. 4
      packages/rocketchat-e2e/server/methods/setUserPublicAndPivateKeys.js
  12. 8
      packages/rocketchat-e2e/server/methods/updateGroupKey.js
  13. 15
      packages/rocketchat-e2e/server/models/Rooms.js
  14. 40
      packages/rocketchat-e2e/server/models/Subscriptions.js
  15. 44
      packages/rocketchat-e2e/server/models/Users.js
  16. 4
      packages/rocketchat-e2e/server/settings.js
  17. 14
      packages/rocketchat-models/server/models/Rooms.js
  18. 39
      packages/rocketchat-models/server/models/Subscriptions.js
  19. 43
      packages/rocketchat-models/server/models/Users.js

@ -2,13 +2,13 @@ import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import toastr from 'toastr';
import s from 'underscore.string';
import { RocketChat } from 'meteor/rocketchat:lib';
import { settings } from 'meteor/rocketchat:settings';
import { t } from 'meteor/rocketchat:utils';
import { e2e } from 'meteor/rocketchat:e2e';
Template.accountEncryption.helpers({
isEnabled() {
return RocketChat.settings.get('E2E_Enable');
return settings.get('E2E_Enable');
},
allowKeyChange() {
return localStorage.getItem('public_key') && localStorage.getItem('private_key');

@ -3,9 +3,12 @@ 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 { RocketChat, call } from 'meteor/rocketchat:lib';
import { Rooms, Subscriptions, Messages } from 'meteor/rocketchat:models';
import { promises } from 'meteor/rocketchat:promises';
import { settings } from 'meteor/rocketchat:settings';
import { Notifications } from 'meteor/rocketchat:notifications';
import { Layout, call } from 'meteor/rocketchat:ui-utils';
import { TAPi18n } from 'meteor/tap:i18n';
import { E2ERoom } from './rocketchat.e2e.room';
import {
@ -59,7 +62,7 @@ class E2E {
return;
}
const room = RocketChat.models.Rooms.findOne({
const room = Rooms.findOne({
_id: roomId,
});
@ -72,7 +75,7 @@ class E2E {
}
if (!this.instancesByRoomId[roomId]) {
const subscription = RocketChat.models.Subscriptions.findOne({
const subscription = Subscriptions.findOne({
rid: roomId,
});
@ -206,7 +209,7 @@ class E2E {
}
setupListeners() {
RocketChat.Notifications.onUser('e2ekeyRequest', async(roomId, keyId) => {
Notifications.onUser('e2ekeyRequest', async(roomId, keyId) => {
const e2eRoom = await this.getInstanceByRoomId(roomId);
if (!e2eRoom) {
return;
@ -215,19 +218,19 @@ class E2E {
e2eRoom.provideKeyToUser(keyId);
});
RocketChat.models.Subscriptions.after.update((userId, doc) => {
Subscriptions.after.update((userId, doc) => {
this.decryptSubscription(doc);
});
RocketChat.models.Subscriptions.after.insert((userId, doc) => {
Subscriptions.after.insert((userId, doc) => {
this.decryptSubscription(doc);
});
RocketChat.models.Messages.after.update((userId, doc) => {
Messages.after.update((userId, doc) => {
this.decryptMessage(doc);
});
RocketChat.models.Messages.after.insert((userId, doc) => {
Messages.after.insert((userId, doc) => {
this.decryptMessage(doc);
});
}
@ -421,7 +424,7 @@ class E2E {
return;
}
RocketChat.models.Messages.direct.update({ _id: message._id }, {
Messages.direct.update({ _id: message._id }, {
$set: {
msg: data.text,
e2e: 'done',
@ -434,7 +437,7 @@ class E2E {
return;
}
return await RocketChat.models.Messages.find({ t: 'e2e', e2e: 'pending' }).forEach(async(item) => {
return await Messages.find({ t: 'e2e', e2e: 'pending' }).forEach(async(item) => {
await this.decryptMessage(item);
});
}
@ -459,7 +462,7 @@ class E2E {
return;
}
RocketChat.models.Subscriptions.direct.update({
Subscriptions.direct.update({
_id: subscription._id,
}, {
$set: {
@ -470,7 +473,7 @@ class E2E {
}
async decryptPendingSubscriptions() {
RocketChat.models.Subscriptions.find({
Subscriptions.find({
'lastMessage.t': 'e2e',
'lastMessage.e2e': {
$ne: 'done',
@ -494,9 +497,9 @@ export const e2e = new E2E();
Meteor.startup(function() {
Tracker.autorun(function() {
if (Meteor.userId()) {
const adminEmbedded = RocketChat.Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin');
const adminEmbedded = Layout.isEmbedded() && FlowRouter.current().path.startsWith('/admin');
if (!adminEmbedded && RocketChat.settings.get('E2E_Enable') && window.crypto) {
if (!adminEmbedded && settings.get('E2E_Enable') && window.crypto) {
e2e.startClient();
e2e.enabled.set(true);
} else {
@ -506,7 +509,7 @@ Meteor.startup(function() {
});
// Encrypt messages before sending
RocketChat.promises.add('onClientBeforeSendMessage', async function(message) {
promises.add('onClientBeforeSendMessage', async function(message) {
if (!message.rid) {
return Promise.resolve(message);
}
@ -525,5 +528,5 @@ Meteor.startup(function() {
message.e2e = 'pending';
return message;
});
}, RocketChat.promises.priority.HIGH);
}, promises.priority.HIGH);
});

@ -5,8 +5,9 @@ import { ReactiveVar } from 'meteor/reactive-var';
import { EJSON } from 'meteor/ejson';
import { Random } from 'meteor/random';
import { TimeSync } from 'meteor/mizzao:timesync';
import { RocketChat, call } from 'meteor/rocketchat:lib';
import { Notifications } from 'meteor/rocketchat:notifications';
import { Rooms, Subscriptions } from 'meteor/rocketchat:models';
import { call } from 'meteor/rocketchat:ui-utils';
import { e2e } from './rocketchat.e2e';
import {
Deferred,
@ -38,7 +39,7 @@ export class E2ERoom {
this._ready.set(true);
this.establishing.set(false);
RocketChat.Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => {
Notifications.onRoom(this.roomId, 'e2ekeyRequest', async(keyId) => {
this.provideKeyToUser(keyId);
});
});
@ -70,7 +71,7 @@ export class E2ERoom {
// Fetch encrypted session key from subscription model
let groupKey;
try {
groupKey = RocketChat.models.Subscriptions.findOne({ rid: this.roomId }).E2EKey;
groupKey = Subscriptions.findOne({ rid: this.roomId }).E2EKey;
} catch (error) {
return console.error('E2E -> Error fetching group key: ', error);
}
@ -81,7 +82,7 @@ export class E2ERoom {
return true;
}
const room = RocketChat.models.Rooms.findOne({ _id: this.roomId });
const room = Rooms.findOne({ _id: this.roomId });
if (!room.e2eKeyId) {
await this.createGroupKey();
@ -92,7 +93,7 @@ export class E2ERoom {
console.log('E2E -> Requesting room key');
// TODO: request group key
RocketChat.Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId);
Notifications.notifyUsersOfRoom(this.roomId, 'e2ekeyRequest', this.roomId, room.e2eKeyId);
}
isSupportedRoomType(type) {

@ -10,8 +10,14 @@ Package.onUse(function(api) {
'ecmascript',
'less',
'mizzao:timesync',
'rocketchat:lib',
'rocketchat:utils',
'rocketchat:models',
'rocketchat:notifications',
'rocketchat:authorization',
'rocketchat:callbacks',
'rocketchat:settings',
'rocketchat:promises',
'rocketchat:ui-utils',
'templating',
'sha',
]);

@ -1,9 +1,7 @@
import { RocketChat } from 'meteor/rocketchat:lib';
import { callbacks } from 'meteor/rocketchat:callbacks';
import { Notifications } from 'meteor/rocketchat:notifications';
import './settings';
import './models/Users';
import './models/Rooms';
import './models/Subscriptions';
import './methods/setUserPublicAndPivateKeys';
import './methods/getUsersOfRoomWithoutKey';
import './methods/updateGroupKey';
@ -12,6 +10,6 @@ import './methods/fetchMyKeys';
import './methods/resetUserE2EKey';
import './methods/requestSubscriptionKeys';
RocketChat.callbacks.add('afterJoinRoom', (user, room) => {
RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId);
callbacks.add('afterJoinRoom', (user, room) => {
Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId);
});

@ -1,5 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Users } from 'meteor/rocketchat:models';
Meteor.methods({
'e2e.fetchMyKeys'() {
@ -7,6 +7,6 @@ Meteor.methods({
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.fetchMyKeys' });
}
return RocketChat.models.Users.fetchKeysByUserId(userId);
return Users.fetchKeysByUserId(userId);
},
});

@ -1,5 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Subscriptions, Users } from 'meteor/rocketchat:models';
Meteor.methods({
'e2e.getUsersOfRoomWithoutKey'(rid) {
@ -13,11 +13,11 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.getUsersOfRoomWithoutKey' });
}
const subscriptions = RocketChat.models.Subscriptions.findByRidWithoutE2EKey(rid, { fields: { 'u._id': 1 } }).fetch();
const subscriptions = Subscriptions.findByRidWithoutE2EKey(rid, { fields: { 'u._id': 1 } }).fetch();
const userIds = subscriptions.map((s) => s.u._id);
const options = { fields: { 'e2e.public_key': 1 } };
const users = RocketChat.models.Users.findByIdsWithPublicE2EKey(userIds, options).fetch();
const users = Users.findByIdsWithPublicE2EKey(userIds, options).fetch();
return {
users,

@ -1,5 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Subscriptions, Rooms } from 'meteor/rocketchat:models';
import { Notifications } from 'meteor/rocketchat:notifications';
Meteor.methods({
'e2e.requestSubscriptionKeys'() {
@ -10,7 +11,7 @@ Meteor.methods({
}
// Get all encrypted rooms that the user is subscribed to and has no E2E key yet
const subscriptions = RocketChat.models.Subscriptions.findByUserIdWithoutE2E(Meteor.userId());
const subscriptions = Subscriptions.findByUserIdWithoutE2E(Meteor.userId());
const roomIds = subscriptions.map((subscription) => subscription.rid);
// For all subscriptions without E2E key, get the rooms that have encryption enabled
@ -23,9 +24,9 @@ Meteor.methods({
},
};
const rooms = RocketChat.models.Rooms.find(query);
const rooms = Rooms.find(query);
rooms.forEach((room) => {
RocketChat.Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId);
Notifications.notifyRoom('e2e.keyRequest', room._id, room.e2eKeyId);
});
return true;

@ -1,5 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Users, Subscriptions } from 'meteor/rocketchat:models';
import { hasPermission } from 'meteor/rocketchat:authorization';
Meteor.methods({
'e2e.resetUserE2EKey'(userId) {
@ -9,17 +10,17 @@ Meteor.methods({
});
}
if (RocketChat.authz.hasPermission(Meteor.userId(), 'reset-other-user-e2e-key') !== true) {
if (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);
RocketChat.models.Subscriptions.resetUserE2EKey(userId);
Users.resetE2EKey(userId);
Subscriptions.resetUserE2EKey(userId);
// Force the user to logout, so that the keys can be generated again
RocketChat.models.Users.removeResumeService(userId);
Users.removeResumeService(userId);
return true;
},
});

@ -1,5 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Rooms } from 'meteor/rocketchat:models';
Meteor.methods({
'e2e.setRoomKeyID'(rid, keyID) {
@ -17,6 +17,6 @@ Meteor.methods({
throw new Meteor.Error('error-room-e2e-key-already-exists', 'E2E Key ID already exists', { method: 'e2e.setRoomKeyID' });
}
return RocketChat.models.Rooms.setE2eKeyId(room._id, keyID);
return Rooms.setE2eKeyId(room._id, keyID);
},
});

@ -1,5 +1,5 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Users } from 'meteor/rocketchat:models';
Meteor.methods({
'e2e.setUserPublicAndPivateKeys'({ public_key, private_key }) {
@ -9,6 +9,6 @@ Meteor.methods({
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'e2e.setUserPublicAndPivateKeys' });
}
return RocketChat.models.Users.setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key });
return Users.setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key });
},
});

@ -1,13 +1,13 @@
import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { Subscriptions } from 'meteor/rocketchat:models';
Meteor.methods({
'e2e.updateGroupKey'(rid, uid, key) {
const mySub = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId());
const mySub = Subscriptions.findOneByRoomIdAndUserId(rid, Meteor.userId());
if (mySub) { // I have a subscription to this room
const userSub = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(rid, uid);
const userSub = Subscriptions.findOneByRoomIdAndUserId(rid, uid);
if (userSub) { // uid also has subscription to this room
return RocketChat.models.Subscriptions.updateGroupE2EKey(userSub._id, key);
return Subscriptions.updateGroupE2EKey(userSub._id, key);
}
}
},

@ -1,15 +0,0 @@
import { RocketChat } from 'meteor/rocketchat:lib';
RocketChat.models.Rooms.setE2eKeyId = function(_id, e2eKeyId, options) {
const query = {
_id,
};
const update = {
$set: {
e2eKeyId,
},
};
return this.update(query, update, options);
};

@ -1,40 +0,0 @@
import { RocketChat } from 'meteor/rocketchat:lib';
RocketChat.models.Subscriptions.updateGroupE2EKey = function(_id, key) {
const query = { _id };
const update = { $set: { E2EKey: key } };
this.update(query, update);
return this.findOne({ _id });
};
RocketChat.models.Subscriptions.findByRidWithoutE2EKey = function(rid, options) {
const query = {
rid,
E2EKey: {
$exists: false,
},
};
return this.find(query, options);
};
RocketChat.models.Subscriptions.resetUserE2EKey = function(userId) {
this.update({ 'u._id': userId }, {
$unset: {
E2EKey: '',
},
}, {
multi: true,
});
};
RocketChat.models.Subscriptions.findByUserIdWithoutE2E = function(userId, options) {
const query = {
'u._id': userId,
E2EKey: {
$exists: false,
},
};
return this.find(query, options);
};

@ -1,44 +0,0 @@
import { RocketChat } from 'meteor/rocketchat:lib';
RocketChat.models.Users.setE2EPublicAndPivateKeysByUserId = function(userId, { public_key, private_key }) {
this.update({ _id: userId }, {
$set: {
'e2e.public_key': public_key,
'e2e.private_key': private_key,
},
});
};
RocketChat.models.Users.fetchKeysByUserId = function(userId) {
const user = this.findOne({ _id: userId }, { fields: { e2e: 1 } });
if (!user || !user.e2e || !user.e2e.public_key) {
return {};
}
return {
public_key: user.e2e.public_key,
private_key: user.e2e.private_key,
};
};
RocketChat.models.Users.findByIdsWithPublicE2EKey = function(ids, options) {
const query = {
_id: {
$in: ids,
},
'e2e.public_key': {
$exists: 1,
},
};
return this.find(query, options);
};
RocketChat.models.Users.resetE2EKey = function(userId) {
this.update({ _id: userId }, {
$unset: {
e2e: '',
},
});
};

@ -1,6 +1,6 @@
import { RocketChat } from 'meteor/rocketchat:lib';
import { settings } from 'meteor/rocketchat:settings';
RocketChat.settings.addGroup('E2E Encryption', function() {
settings.addGroup('E2E Encryption', function() {
this.add('E2E_Enable', false, {
type: 'boolean',
i18nLabel: 'Enabled',

@ -26,6 +26,20 @@ export class Rooms extends Base {
return this.findOne(query, options);
}
setE2eKeyId(_id, e2eKeyId, options) {
const query = {
_id,
};
const update = {
$set: {
e2eKeyId,
},
};
return this.update(query, update, options);
}
findOneByImportId(_id, options) {
const query = { importIds: _id };

@ -41,6 +41,45 @@ export class Subscriptions extends Base {
return query;
}
findByRidWithoutE2EKey(rid, options) {
const query = {
rid,
E2EKey: {
$exists: false,
},
};
return this.find(query, options);
}
resetUserE2EKey(userId) {
this.update({ 'u._id': userId }, {
$unset: {
E2EKey: '',
},
}, {
multi: true,
});
}
findByUserIdWithoutE2E(userId, options) {
const query = {
'u._id': userId,
E2EKey: {
$exists: false,
},
};
return this.find(query, options);
}
updateGroupE2EKey(_id, key) {
const query = { _id };
const update = { $set: { E2EKey: key } };
this.update(query, update);
return this.findOne({ _id });
}
findUsersInRoles(roles, scope, options) {
roles = [].concat(roles);

@ -23,6 +23,49 @@ export class Users extends Base {
return { _id: userId };
}
setE2EPublicAndPivateKeysByUserId(userId, { public_key, private_key }) {
this.update({ _id: userId }, {
$set: {
'e2e.public_key': public_key,
'e2e.private_key': private_key,
},
});
}
fetchKeysByUserId(userId) {
const user = this.findOne({ _id: userId }, { fields: { e2e: 1 } });
if (!user || !user.e2e || !user.e2e.public_key) {
return {};
}
return {
public_key: user.e2e.public_key,
private_key: user.e2e.private_key,
};
}
findByIdsWithPublicE2EKey(ids, options) {
const query = {
_id: {
$in: ids,
},
'e2e.public_key': {
$exists: 1,
},
};
return this.find(query, options);
}
resetE2EKey(userId) {
this.update({ _id: userId }, {
$unset: {
e2e: '',
},
});
}
findUsersInRoles(roles, scope, options) {
roles = [].concat(roles);

Loading…
Cancel
Save