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

feat: E2EE messages mentions #32510

Merged
merged 31 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1976fff
feat: E2EE messages mentions
hugocostadev May 28, 2024
24cdb06
Merge remote-tracking branch 'origin/develop' into feat/e2e-messages-…
hugocostadev May 28, 2024
1dafb56
adding E2E_Enabled_Mentions settings
hugocostadev May 28, 2024
5442f41
fix: setting description
hugocostadev Jun 3, 2024
f3656fc
test: adding mention tests
hugocostadev Jun 3, 2024
85a4786
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 3, 2024
d12a819
Create late-planes-sniff.md
hugocostadev Jun 3, 2024
e98ddaf
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 4, 2024
184d931
test: fix flaky keypress behavior
hugocostadev Jun 5, 2024
8c68d7d
fix changeset
hugocostadev Jun 12, 2024
b94db45
Merge remote-tracking branch 'origin/develop' into feat/e2e-messages-…
hugocostadev Jun 14, 2024
9f4c217
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 17, 2024
a97716a
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 18, 2024
cc5425f
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 19, 2024
ad187fa
fix: changeset
hugocostadev Jun 20, 2024
912b80e
fix: e2e test
hugocostadev Jun 20, 2024
701b5ab
fix: getting username
hugocostadev Jun 20, 2024
0bca0d1
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jun 21, 2024
66bc1dd
fix: ts check
hugocostadev Jun 21, 2024
5098368
Merge remote-tracking branch 'origin' into feat/e2e-messages-mentions
hugocostadev Jun 24, 2024
8d1cf4d
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jul 2, 2024
b961662
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jul 10, 2024
5b2267a
Merge remote-tracking branch 'origin/develop' into feat/e2e-messages-…
hugocostadev Jul 10, 2024
7b5b337
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jul 11, 2024
ea93b90
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jul 12, 2024
c8d67bf
Merge branch 'develop' into feat/e2e-messages-mentions
hugocostadev Jul 15, 2024
ad582e9
fix: isE2EEMessage helper usage
hugocostadev Jul 25, 2024
6e374c3
test: add user and channel test case
hugocostadev Jul 25, 2024
887d710
Merge remote-tracking branch 'origin/develop' into feat/e2e-messages-…
hugocostadev Jul 25, 2024
eed82a3
Merge remote-tracking branch 'origin/develop' into feat/e2e-messages-…
hugocostadev Aug 27, 2024
1eeff2d
Merge branch 'develop' into feat/e2e-messages-mentions
kodiakhq[bot] Sep 17, 2024
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
8 changes: 8 additions & 0 deletions .changeset/late-planes-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/core-typings": patch
"@rocket.chat/i18n": patch
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
---

Added the possibility to enable mentions in End to end encrypted channels
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved

2 changes: 1 addition & 1 deletion apps/meteor/app/lib/server/methods/updateMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP
import { settings } from '../../../settings/server';
import { updateMessage } from '../functions/updateMessage';

const allowedEditedFields = ['tshow', 'alias', 'attachments', 'avatar', 'emoji', 'msg', 'customFields'];
const allowedEditedFields = ['tshow', 'alias', 'attachments', 'avatar', 'emoji', 'msg', 'customFields', 'e2eMentions'];

export async function executeUpdateMessage(uid: IUser['_id'], message: AtLeast<IMessage, '_id' | 'rid' | 'msg'>, previewUrls?: string[]) {
const originalMessage = await Messages.findOneById(message._id);
Expand Down
20 changes: 16 additions & 4 deletions apps/meteor/app/mentions/server/Mentions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,17 @@ export class MentionsServer extends MentionsParser {
});
}

async getUsersByMentions({ msg, rid, u: sender }: Pick<IMessage, 'msg' | 'rid' | 'u'>): Promise<IMessage['mentions']> {
const mentions = this.getUserMentions(msg);
async getUsersByMentions({
msg,
rid,
u: sender,
t,
e2eMentions,
}: Pick<IMessage, 'msg' | 'rid' | 'u' | 't' | 'e2eMentions'>): Promise<IMessage['mentions']> {
const mentions =
t === 'e2e' && e2eMentions?.e2eUserMentions && e2eMentions?.e2eUserMentions.length > 0
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
? e2eMentions?.e2eUserMentions
: this.getUserMentions(msg);
const mentionsAll: { _id: string; username: string }[] = [];
const userMentions = [];

Expand All @@ -67,8 +76,11 @@ export class MentionsServer extends MentionsParser {
return [...mentionsAll, ...(userMentions.length ? await this.getUsers(userMentions) : [])];
}

async getChannelbyMentions({ msg }: Pick<IMessage, 'msg'>) {
const channels = this.getChannelMentions(msg);
async getChannelbyMentions({ msg, t, e2eMentions }: Pick<IMessage, 'msg' | 't' | 'e2eMentions'>) {
const channels =
t === 'e2e' && e2eMentions?.e2eChannelMentions && e2eMentions?.e2eChannelMentions.length > 0
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
? e2eMentions?.e2eChannelMentions
: this.getChannelMentions(msg);
return this.getChannels(channels.map((c) => c.trim().substr(1)));
}

Expand Down
25 changes: 24 additions & 1 deletion apps/meteor/client/startup/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { Tracker } from 'meteor/tracker';

import { E2EEState } from '../../app/e2e/client/E2EEState';
import { e2e } from '../../app/e2e/client/rocketchat.e2e';
import { Subscriptions, ChatRoom } from '../../app/models/client';
import { MentionsParser } from '../../app/mentions/lib/MentionsParser';
import { Subscriptions, ChatRoom, Users } from '../../app/models/client';
import { settings } from '../../app/settings/client';
import { sdk } from '../../app/utils/client/lib/SDKClient';
import { onClientBeforeSendMessage } from '../lib/onClientBeforeSendMessage';
Expand Down Expand Up @@ -146,11 +147,33 @@ Meteor.startup(() => {
return message;
}

const mentionsEnabled = settings.get<boolean>('E2E_Enabled_Mentions');

if (mentionsEnabled) {
const me = (message.u?._id && Users.findOne(message.u?._id, { fields: { username: 1 } })?.username) || '';
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
const pattern = settings.get('UTF8_User_Names_Validation');
const useRealName = settings.get('UI_Use_Real_Name');

const mentions = new MentionsParser({
pattern: () => pattern,
useRealName: () => useRealName,
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
me: () => me,
});

const e2eMentions: IMessage['e2eMentions'] = {
e2eUserMentions: mentions.getUserMentions(message.msg),
e2eChannelMentions: mentions.getChannelMentions(message.msg),
};

message.e2eMentions = e2eMentions;
}

// Should encrypt this message.
const msg = await e2eRoom.encrypt(message);
message.msg = msg;
message.t = 'e2e';
message.e2e = 'pending';

return message;
});

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions apps/meteor/server/settings/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ export const createE2ESettings = () =>
public: true,
enableQuery: { _id: 'E2E_Enable', value: true },
});

await this.add('E2E_Enabled_Mentions', false, {
type: 'boolean',
public: true,
enableQuery: { _id: 'E2E_Enable', value: true },
});
});
51 changes: 50 additions & 1 deletion apps/meteor/tests/e2e/e2e-encryption.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,13 @@ test.describe.serial('e2e-encryption', () => {

test.beforeAll(async ({ api }) => {
expect((await api.post('/settings/E2E_Allow_Unencrypted_Messages', { value: true })).status()).toBe(200);
expect((await api.post('/settings/E2E_Enabled_Mentions', { value: true })).status()).toBe(200);
});

test.afterAll(async ({ api }) => {
expect((await api.post('/settings/E2E_Enable', { value: false })).status()).toBe(200);
expect((await api.post('/settings/E2E_Allow_Unencrypted_Messages', { value: false })).status()).toBe(200);
expect((await api.post('/settings/E2E_Enabled_Mentions', { value: false })).status()).toBe(200);
});

test('expect create a private channel encrypted and send an encrypted message', async ({ page }) => {
Expand Down Expand Up @@ -205,6 +207,54 @@ test.describe.serial('e2e-encryption', () => {
await expect(poHomeChannel.content.lastUserMessage.locator('.rcx-icon--name-key')).toBeVisible();
});

test('expect create a encrypted private channel and mention user', async ({ page }) => {
const channelName = faker.string.uuid();

await poHomeChannel.sidenav.createEncryptedChannel(channelName);

await expect(page).toHaveURL(`/group/${channelName}`);

await poHomeChannel.dismissToast();

await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();

await poHomeChannel.content.sendMessage('hello @user1');

const userMention = await page.getByRole('button', {
name: 'user1',
});

await expect(userMention).toBeVisible();

await userMention.click();

await expect(poHomeChannel.content.userCard).toBeVisible();
});

test('expect create a encrypted private channel, mention a channel and navigate to it', async ({ page }) => {
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
const channelName = faker.string.uuid();

await poHomeChannel.sidenav.createEncryptedChannel(channelName);

await expect(page).toHaveURL(`/group/${channelName}`);

await poHomeChannel.dismissToast();

await expect(poHomeChannel.content.encryptedRoomHeaderIcon).toBeVisible();

await poHomeChannel.content.sendMessage('Are you in the #general channel?');

const channelMention = await page.getByRole('button', {
name: 'general',
});

await expect(channelMention).toBeVisible();

await channelMention.click();
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved

await expect(page).toHaveURL(`/channel/general`);
});

test('expect placeholder text in place of encrypted message, when E2EE is not setup', async ({ page }) => {
const channelName = faker.string.uuid();

Expand Down Expand Up @@ -447,5 +497,4 @@ test.describe.serial('e2ee room setup', () => {
await expect(poHomeChannel.content.inputMessage).not.toBeVisible();
await expect(page.locator('.rcx-states__title')).toContainText('Check back later');
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ export class HomeContent {
async sendMessage(text: string): Promise<void> {
await this.joinRoomIfNeeded();
await this.page.waitForSelector('[name="msg"]:not([disabled])');
await this.page.locator('[name="msg"]').type(text);
await this.page.keyboard.press('Enter');
await this.page.locator('[name="msg"]').fill(text);
await this.page.getByLabel('Send').click();
}

async dispatchSlashCommand(text: string): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions packages/core-typings/src/IMessage/IMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export interface IMessage extends IRocketChatRecord {
tcount?: number;
t?: MessageTypesValues;
e2e?: 'pending' | 'done';
e2eMentions?: { e2eUserMentions?: string[]; e2eChannelMentions?: string[] };
otrAck?: string;

urls?: MessageUrl[];
Expand Down
2 changes: 2 additions & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,8 @@
"E2E_Enabled": "E2E Enabled",
"E2E_Enabled_Default_DirectRooms": "Enable encryption for Direct Rooms by default",
"E2E_Enabled_Default_PrivateRooms": "Enable encryption for Private Rooms by default",
"E2E_Enabled_Mentions": "Mentions",
"E2E_Enabled_Mentions_Description": "Notify people, and highlight user, channel, and team mentions in encrypted content.",
"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_key_reset_email": "E2E Key Reset Notification",
Expand Down
Loading