From 12fe0c3159dbd37175de1d27123e3b9903685542 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Fri, 20 Mar 2020 02:08:04 -0300 Subject: [PATCH] [NEW] Settings to enable E2E encryption for Private and Direct Rooms by default (#16928) --- app/cas/server/cas_server.js | 17 +--- app/e2e/server/beforeCreateRoom.js | 11 +++ app/e2e/server/index.js | 1 + app/e2e/server/settings.js | 10 ++ app/lib/server/functions/createDirectRoom.js | 50 ++++++++-- app/lib/server/functions/createRoom.js | 8 +- .../server/saml_server.js | 18 +--- packages/rocketchat-i18n/i18n/en.i18n.json | 2 + packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 2 + server/methods/createDirectMessage.js | 91 +------------------ 10 files changed, 81 insertions(+), 129 deletions(-) create mode 100644 app/e2e/server/beforeCreateRoom.js diff --git a/app/cas/server/cas_server.js b/app/cas/server/cas_server.js index de2bec2ff5ff..50828e58240a 100644 --- a/app/cas/server/cas_server.js +++ b/app/cas/server/cas_server.js @@ -2,7 +2,6 @@ import url from 'url'; import { Meteor } from 'meteor/meteor'; import { Accounts } from 'meteor/accounts-base'; -import { Random } from 'meteor/random'; import { WebApp } from 'meteor/webapp'; import { RoutePolicy } from 'meteor/routepolicy'; import _ from 'underscore'; @@ -11,8 +10,9 @@ import CAS from 'cas'; import { logger } from './cas_rocketchat'; import { settings } from '../../settings'; -import { Rooms, Subscriptions, CredentialTokens } from '../../models'; +import { Rooms, CredentialTokens } from '../../models/server'; import { _setRealName } from '../../lib'; +import { createRoom } from '../../lib/server/functions/createRoom'; RoutePolicy.declare('/_cas/', 'network'); @@ -250,18 +250,7 @@ Accounts.registerLoginHandler(function(options) { if (room_name) { let room = Rooms.findOneByNameAndType(room_name, 'c'); if (!room) { - room = Rooms.createWithIdTypeAndName(Random.id(), 'c', room_name); - } - - if (!Subscriptions.findOneByRoomIdAndUserId(room._id, userId)) { - Subscriptions.createWithRoomAndUser(room, user, { - ts: new Date(), - open: true, - alert: true, - unread: 1, - userMentions: 1, - groupMentions: 0, - }); + room = createRoom('c', room_name, user.username); } } }); diff --git a/app/e2e/server/beforeCreateRoom.js b/app/e2e/server/beforeCreateRoom.js new file mode 100644 index 000000000000..29ee95653d42 --- /dev/null +++ b/app/e2e/server/beforeCreateRoom.js @@ -0,0 +1,11 @@ +import { callbacks } from '../../callbacks/server'; +import { settings } from '../../settings/server'; + +callbacks.add('beforeCreateRoom', ({ type, extraData }) => { + if ( + (type === 'd' && settings.get('E2E_Enabled_Default_DirectRooms')) + || (type === 'p' && settings.get('E2E_Enabled_Default_PrivateRooms')) + ) { + extraData.encrypted = true; + } +}); diff --git a/app/e2e/server/index.js b/app/e2e/server/index.js index 6f2ab21df038..47f3d8df836a 100644 --- a/app/e2e/server/index.js +++ b/app/e2e/server/index.js @@ -2,6 +2,7 @@ import { callbacks } from '../../callbacks'; import { Notifications } from '../../notifications'; import './settings'; +import './beforeCreateRoom'; import './methods/setUserPublicAndPrivateKeys'; import './methods/getUsersOfRoomWithoutKey'; import './methods/updateGroupKey'; diff --git a/app/e2e/server/settings.js b/app/e2e/server/settings.js index 2d9b508e4764..e15f5e4df1ab 100644 --- a/app/e2e/server/settings.js +++ b/app/e2e/server/settings.js @@ -8,4 +8,14 @@ settings.addGroup('E2E Encryption', function() { public: true, alert: 'E2E_Enable_alert', }); + + this.add('E2E_Enabled_Default_DirectRooms', false, { + type: 'boolean', + enableQuery: { _id: 'E2E_Enable', value: true }, + }); + + this.add('E2E_Enabled_Default_PrivateRooms', false, { + type: 'boolean', + enableQuery: { _id: 'E2E_Enable', value: true }, + }); }); diff --git a/app/lib/server/functions/createDirectRoom.js b/app/lib/server/functions/createDirectRoom.js index 67d516adff7d..a209e841569b 100644 --- a/app/lib/server/functions/createDirectRoom.js +++ b/app/lib/server/functions/createDirectRoom.js @@ -1,45 +1,81 @@ import { Rooms, Subscriptions } from '../../../models/server'; +import { getDefaultSubscriptionPref } from '../../../utils/server'; +import { callbacks } from '../../../callbacks/server'; export const createDirectRoom = function(source, target, extraData, options) { const rid = [source._id, target._id].sort().join(''); - Rooms.upsert({ _id: rid }, { + const now = new Date(); + + const roomUpsertResult = Rooms.upsert({ _id: rid }, { + $set: { + usernames: [source.username, target.username], + }, $setOnInsert: Object.assign({ t: 'd', - usernames: [source.username, target.username], msgs: 0, - ts: new Date(), + ts: now, + usersCount: 2, }, extraData), }); + const targetNotificationPref = getDefaultSubscriptionPref(target); + Subscriptions.upsert({ rid, 'u._id': target._id }, { $setOnInsert: Object.assign({ + fname: source.name, name: source.username, t: 'd', - open: true, - alert: true, + open: false, + alert: false, unread: 0, + userMentions: 0, + groupMentions: 0, + customFields: target.customFields, u: { _id: target._id, username: target.username, }, + ts: now, + ...targetNotificationPref, }, options.subscriptionExtra), }); + const sourceNotificationPref = getDefaultSubscriptionPref(source); + Subscriptions.upsert({ rid, 'u._id': source._id }, { + $set: { + ls: now, + open: true, + ...target.active === false && { + archived: true, + }, + }, $setOnInsert: Object.assign({ + fname: target.name, name: target.username, t: 'd', - open: true, - alert: true, + alert: false, unread: 0, + userMentions: 0, + groupMentions: 0, + customFields: source.customFields, u: { _id: source._id, username: source.username, }, + ts: now, + ...sourceNotificationPref, }, options.subscriptionExtra), }); + // If the room is new, run a callback + if (roomUpsertResult.insertedId) { + const insertedRoom = Rooms.findOneById(rid); + + callbacks.run('afterCreateDirectRoom', insertedRoom, { from: source, to: target }); + } + return { _id: rid, t: 'd', diff --git a/app/lib/server/functions/createRoom.js b/app/lib/server/functions/createRoom.js index 434ecc8ac086..e82cb8806fbb 100644 --- a/app/lib/server/functions/createRoom.js +++ b/app/lib/server/functions/createRoom.js @@ -9,7 +9,9 @@ import { getValidRoomName } from '../../../utils'; import { Apps } from '../../../apps/server'; import { createDirectRoom } from './createDirectRoom'; -export const createRoom = function(type, name, owner, members, readOnly, extraData = {}, options = {}) { +export const createRoom = function(type, name, owner, members = [], readOnly, extraData = {}, options = {}) { + callbacks.run('beforeCreateRoom', { type, name, owner, members, readOnly, extraData, options }); + if (type === 'd') { return createDirectRoom(members[0], members[1], extraData, options); } @@ -59,10 +61,6 @@ export const createRoom = function(type, name, owner, members, readOnly, extraDa ro: readOnly === true, }); - if (type === 'd') { - room.usernames = members; - } - if (Apps && Apps.isLoaded()) { const prevent = Promise.await(Apps.getBridges().getListenerBridge().roomEvent('IPreRoomCreatePrevent', room)); if (prevent) { diff --git a/app/meteor-accounts-saml/server/saml_server.js b/app/meteor-accounts-saml/server/saml_server.js index 4d0e716b273e..ca1e6544713a 100644 --- a/app/meteor-accounts-saml/server/saml_server.js +++ b/app/meteor-accounts-saml/server/saml_server.js @@ -11,10 +11,9 @@ import s from 'underscore.string'; import { SAML } from './saml_utils'; import { settings } from '../../settings/server'; -import { Rooms, Subscriptions, CredentialTokens } from '../../models'; +import { Users, Rooms, CredentialTokens } from '../../models/server'; import { generateUsernameSuggestion } from '../../lib'; -import { _setUsername } from '../../lib/server/functions'; -import { Users } from '../../models/server'; +import { _setUsername, createRoom } from '../../lib/server/functions'; if (!Accounts.saml) { Accounts.saml = { @@ -419,18 +418,7 @@ Accounts.saml.subscribeToSAMLChannels = function(channels, user) { let room = Rooms.findOneByNameAndType(roomName, 'c'); if (!room) { - room = Rooms.createWithIdTypeAndName(Random.id(), 'c', roomName); - } - - if (!Subscriptions.findOneByRoomIdAndUserId(room._id, user._id)) { - Subscriptions.createWithRoomAndUser(room, user, { - ts: new Date(), - open: true, - alert: true, - unread: 1, - userMentions: 1, - groupMentions: 0, - }); + room = createRoom('c', roomName, user.username); } } } catch (err) { diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 66396ccf61b4..8759d94d6399 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1209,6 +1209,8 @@ "E2E_Enabled": "E2E Enabled", "E2E_Enable_alert": "This feature is currently in beta! Please report bugs to github.com/RocketChat/Rocket.Chat/issues and be aware of:
- Encrypted messages of encrypted rooms will not be found by search operations.
- The mobile apps may not support the encypted messages (they are implementing it).
- Bots may not be able to see encrypted messages until they implement support for it.
- Uploads will not be encrypted in this version.", "E2E_Enable_description": "Enable option to create encrypted groups and be able to change groups and direct messages to be encrypted", + "E2E_Enabled_Default_DirectRooms": "Enable encryption for Direct Rooms by default", + "E2E_Enabled_Default_PrivateRooms": "Enable encryption for Private Rooms by default", "E2E_Encryption_Password_Change": "Change Encryption Password", "E2E_Encryption_Password_Explanation": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.

This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store your password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.", "E2E_password_reveal_text": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.

This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store this password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on. Learn more here!

Your password is: %s

This is an auto generated password, you can setup a new password for your encryption key any time from any browser you have entered the existing password.
This password is only stored on this browser until you store the password and dismiss this message.", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index ecf78a99815f..cfbcc744b4d9 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -1125,6 +1125,8 @@ "E2E_Enabled": "E2E Enabled", "E2E_Enable_alert": "Este recurso está atualmente em beta! Relate os erros para github.com/RocketChat/Rocket.Chat/issues e esteja ciente de:
- As mensagens criptografadas de salas criptografadas não serão encontradas pelas operações de pesquisa.
- Os aplicativos para dispositivos móveis talvez não ofereçam suporte às mensagens critografadas (eles estão implementando).
- Os bots talvez não consigam ver as mensagens criptografadas até implementarem o suporte.
- Os uploads não serão criptografados nesta versão.", "E2E_Enable_description": "Ative a opção para criar grupos criptografados e ser capaz de alterar grupos e mensagens privadas para serem criptografadas", + "E2E_Enabled_Default_DirectRooms": "Ativar criptografia em salas diretas por padrão", + "E2E_Enabled_Default_PrivateRooms": "Ativar criptografia em salas privadas por padrão", "E2E_Encryption_Password_Explanation": "Agora você pode criar grupos privados criptografados e mensagens diretas. Você também pode alterar os grupos privados ou DMs existentes para criptografados.

Esta é uma criptografia de ponta a ponta, logo a chave para codificar / decodificar suas mensagens não será salva no servidor. Por esse motivo, você precisa armazenar sua senha em algum lugar seguro. Será solicitada a inserção de senha em outros dispositivos nos quais deseja usar a criptografia E2E.", "E2E_password_reveal_text": "Agora você pode criar grupos privados e mensagens diretas criptografados. Você também pode alterar os grupos privados ou DMs existentes para criptografados.

Esta é uma criptografia de ponta a ponta, logo a chave para codificar / decodificar suas mensagens não será salva no servidor. Por esse motivo, você precisa armazenar sua senha em algum lugar seguro. Será solicitada a inserção de senha em outros dispositivos nos quais deseja usar a criptografia E2E. Saiba mais aqui!

Sua senha é: %s

Esta é uma senha gerada automaticamente, você pode configurar uma nova senha para sua chave de criptografia a qualquer momento, a partir de qualquer navegador onde utilizou a senha existente.
Esta senha só é armazenada neste navegador até que você armazene a senha e feche esta mensagem.", "E2E_password_request_text": "Para acessar seus grupos privados e mensagens diretas criptografados , insira sua senha de criptografia.
Você precisa digitar essa senha para codificar / decodificar suas mensagens em todos os clientes que você usa, já que a chave não está armazenada no servidor.", diff --git a/server/methods/createDirectMessage.js b/server/methods/createDirectMessage.js index c2d2dcc9d17f..9d1d48f92441 100644 --- a/server/methods/createDirectMessage.js +++ b/server/methods/createDirectMessage.js @@ -3,11 +3,10 @@ import { check } from 'meteor/check'; import { settings } from '../../app/settings'; import { hasPermission } from '../../app/authorization'; -import { Users, Rooms, Subscriptions } from '../../app/models'; -import { getDefaultSubscriptionPref } from '../../app/utils'; +import { Users } from '../../app/models'; import { RateLimiter } from '../../app/lib'; -import { callbacks } from '../../app/callbacks'; import { addUser } from '../../app/federation/server/functions/addUser'; +import { createRoom } from '../../app/lib/server'; Meteor.methods({ createDirectMessage(username) { @@ -58,91 +57,7 @@ Meteor.methods({ }); } - const rid = [me._id, to._id].sort().join(''); - - const now = new Date(); - - // Make sure we have a room - const roomUpsertResult = Rooms.upsert({ - _id: rid, - }, { - $set: { - usernames: [me.username, to.username], - }, - $setOnInsert: { - t: 'd', - msgs: 0, - ts: now, - usersCount: 2, - }, - }); - - const myNotificationPref = getDefaultSubscriptionPref(me); - - // Make user I have a subcription to this room - const upsertSubscription = { - $set: { - ls: now, - open: true, - }, - $setOnInsert: { - fname: to.name, - name: to.username, - t: 'd', - alert: false, - unread: 0, - userMentions: 0, - groupMentions: 0, - customFields: me.customFields, - u: { - _id: me._id, - username: me.username, - }, - ts: now, - ...myNotificationPref, - }, - }; - - if (to.active === false) { - upsertSubscription.$set.archived = true; - } - - Subscriptions.upsert({ - rid, - $and: [{ 'u._id': me._id }], // work around to solve problems with upsert and dot - }, upsertSubscription); - - const toNotificationPref = getDefaultSubscriptionPref(to); - - Subscriptions.upsert({ - rid, - $and: [{ 'u._id': to._id }], // work around to solve problems with upsert and dot - }, { - $setOnInsert: { - fname: me.name, - name: me.username, - t: 'd', - open: false, - alert: false, - unread: 0, - userMentions: 0, - groupMentions: 0, - customFields: to.customFields, - u: { - _id: to._id, - username: to.username, - }, - ts: now, - ...toNotificationPref, - }, - }); - - // If the room is new, run a callback - if (roomUpsertResult.insertedId) { - const insertedRoom = Rooms.findOneById(rid); - - callbacks.run('afterCreateDirectRoom', insertedRoom, { from: me, to }); - } + const { _id: rid } = createRoom('d', undefined, undefined, [me, to]); return { rid,