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

[NEW] Settings to enable E2E encryption for Private and Direct Rooms by default #16928

Merged
merged 6 commits into from
Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 3 additions & 14 deletions app/cas/server/cas_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
rodrigok marked this conversation as resolved.
Show resolved Hide resolved
import { _setRealName } from '../../lib';
import { createRoom } from '../../lib/server/functions/createRoom';

RoutePolicy.declare('/_cas/', 'network');

Expand Down Expand Up @@ -249,18 +249,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);
}
}
});
Expand Down
13 changes: 13 additions & 0 deletions app/e2e/server/beforeCreateRoom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { callbacks } from '../../callbacks';
import { settings } from '../../settings';
rodrigok marked this conversation as resolved.
Show resolved Hide resolved

callbacks.add('beforeCreateRoom', ({ type, extraData }) => {
if (['d', 'p'].includes(type)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is kinda happening two times.. I think leaving only the if below makes more sense.

if (
(type === 'd' && settings.get('E2E_Enabled_Default_DirectRooms'))
|| (type === 'p' && settings.get('E2E_Enabled_Default_PrivateRooms'))
) {
extraData.encrypted = true;
}
}
});
1 change: 1 addition & 0 deletions app/e2e/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
10 changes: 10 additions & 0 deletions app/e2e/server/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
});
});
50 changes: 43 additions & 7 deletions app/lib/server/functions/createDirectRoom.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,81 @@
import { Rooms, Subscriptions } from '../../../models/server';
import { getDefaultSubscriptionPref } from '../../../utils';
import { callbacks } from '../../../callbacks';
rodrigok marked this conversation as resolved.
Show resolved Hide resolved

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',
Expand Down
8 changes: 3 additions & 5 deletions app/lib/server/functions/createRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down
17 changes: 3 additions & 14 deletions app/meteor-accounts-saml/server/saml_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import _ from 'underscore';
import s from 'underscore.string';

import { SAML } from './saml_utils';
import { Rooms, Subscriptions, CredentialTokens } from '../../models';
import { Rooms, CredentialTokens } from '../../models';
import { generateUsernameSuggestion } from '../../lib';
import { _setUsername } from '../../lib/server/functions';
import { _setUsername, createRoom } from '../../lib/server/functions';
import { Users } from '../../models/server';

if (!Accounts.saml) {
Expand Down Expand Up @@ -418,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) {
Expand Down
2 changes: 2 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,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:<br/>- Encrypted messages of encrypted rooms will not be found by search operations.<br/>- The mobile apps may not support the encypted messages (they are implementing it).<br/>- Bots may not be able to see encrypted messages until they implement support for it.<br/>- 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.<br/><br/>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.<br/><br/>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. <a href=\"https://rocket.chat/docs/user-guides/end-to-end-encryption/\" target=\"_blank\">Learn more here!</a><br/><br/>Your password is: <span style=\"font-weight: bold;\">%s</span><br/><br/>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.<br/>This password is only stored on this browser until you store the password and dismiss this message.",
Expand Down
2 changes: 2 additions & 0 deletions packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,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: <br/>- As mensagens criptografadas de salas criptografadas não serão encontradas pelas operações de pesquisa. <br/>- Os aplicativos para dispositivos móveis talvez não ofereçam suporte às mensagens critografadas (eles estão implementando). <br/>- Os bots talvez não consigam ver as mensagens criptografadas até implementarem o suporte. <br/>- 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. <br/><br/>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. <br/><br/>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. <a href=\"https://rocket.chat/docs/user-guides/end-to-end-encryption/\" target=\"_blank\">Saiba mais aqui!</a><br/><br/>Sua senha é: <span style=\"font-weight: bold;\">%s</span><br/><br/>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. <br/>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. <br/>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.",
Expand Down
91 changes: 3 additions & 88 deletions server/methods/createDirectMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down