diff --git a/cypress/global.d.ts b/cypress/global.d.ts index efbb255b08..a3e91a2b44 100644 --- a/cypress/global.d.ts +++ b/cypress/global.d.ts @@ -16,7 +16,9 @@ limitations under the License. import "matrix-js-sdk/src/@types/global"; import type { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client"; +import type { MatrixScheduler, MemoryCryptoStore, MemoryStore, RoomStateEvent } from "matrix-js-sdk/src/matrix"; import type { RoomMemberEvent } from "matrix-js-sdk/src/models/room-member"; +import type { WebStorageSessionStore } from "matrix-js-sdk/src/store/session/webstorage"; import type { MatrixDispatcher } from "../src/dispatcher/dispatcher"; import type PerformanceMonitor from "../src/performance"; @@ -35,6 +37,11 @@ declare global { MatrixClient: typeof MatrixClient; ClientEvent: typeof ClientEvent; RoomMemberEvent: typeof RoomMemberEvent; + RoomStateEvent: typeof RoomStateEvent; + MatrixScheduler: typeof MatrixScheduler; + MemoryStore: typeof MemoryStore; + MemoryCryptoStore: typeof MemoryCryptoStore; + WebStorageSessionStore: typeof WebStorageSessionStore; }; } } diff --git a/cypress/integration/7-crypto/crypto.spec.ts b/cypress/integration/7-crypto/crypto.spec.ts new file mode 100644 index 0000000000..2446e3bd2b --- /dev/null +++ b/cypress/integration/7-crypto/crypto.spec.ts @@ -0,0 +1,82 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import type { MatrixClient } from "matrix-js-sdk/src/matrix"; +import { SynapseInstance } from "../../plugins/synapsedocker"; + +function waitForEncryption(cli: MatrixClient, roomId: string, win: Cypress.AUTWindow, resolve: () => void) { + cli.crypto.cryptoStore.getEndToEndRooms(null, (result) => { + if (result[roomId]) { + resolve(); + } else { + cli.once(win.matrixcs.RoomStateEvent.Update, () => waitForEncryption(cli, roomId, win, resolve)); + } + }); +} + +describe("Cryptography", () => { + beforeEach(() => { + cy.startSynapse("default").as('synapse').then( + synapse => cy.initTestUser(synapse, "Alice"), + ); + }); + + afterEach(() => { + cy.get('@synapse').then(synapse => cy.stopSynapse(synapse)); + }); + + it("should receive and decrypt encrypted messages", () => { + cy.get('@synapse').then(synapse => cy.getBot(synapse, "Beatrice").as('bot')); + + cy.createRoom({ + initial_state: [ + { + type: "m.room.encryption", + state_key: '', + content: { + algorithm: "m.megolm.v1.aes-sha2", + }, + }, + ], + }).as('roomId'); + + cy.all([ + cy.get('@bot'), + cy.get('@roomId'), + cy.window(), + ]).then(([bot, roomId, win]) => { + cy.inviteUser(roomId, bot.getUserId()); + cy.visit("/#/room/" + roomId); + cy.wrap( + new Promise(resolve => + waitForEncryption(bot, roomId, win, resolve), + ).then(() => bot.sendMessage(roomId, { + body: "Top secret message", + msgtype: "m.text", + })), + ); + }); + + cy.get(".mx_RoomView_body .mx_cryptoEvent").should("contain", "Encryption enabled"); + + cy.get(".mx_EventTile_body") + .contains("Top secret message") + .closest(".mx_EventTile_line") + .should("not.have.descendants", ".mx_EventTile_e2eIcon_warning"); + }); +}); diff --git a/cypress/support/bot.ts b/cypress/support/bot.ts index a2488c0081..e3bdf49d31 100644 --- a/cypress/support/bot.ts +++ b/cypress/support/bot.ts @@ -20,6 +20,7 @@ import request from "browser-request"; import type { MatrixClient } from "matrix-js-sdk/src/client"; import { SynapseInstance } from "../plugins/synapsedocker"; +import { MockStorage } from "./storage"; import Chainable = Cypress.Chainable; declare global { @@ -47,6 +48,10 @@ Cypress.Commands.add("getBot", (synapse: SynapseInstance, displayName?: string): deviceId: credentials.deviceId, accessToken: credentials.accessToken, request, + store: new win.matrixcs.MemoryStore(), + scheduler: new win.matrixcs.MatrixScheduler(), + cryptoStore: new win.matrixcs.MemoryCryptoStore(), + sessionStore: new win.matrixcs.WebStorageSessionStore(new MockStorage()), }); cli.on(win.matrixcs.RoomMemberEvent.Membership, (event, member) => { @@ -55,9 +60,12 @@ Cypress.Commands.add("getBot", (synapse: SynapseInstance, displayName?: string): } }); - cli.startClient(); - - return cli; + return cy.wrap( + cli.initCrypto() + .then(() => cli.setGlobalErrorOnUnknownDevices(false)) + .then(() => cli.startClient()) + .then(() => cli), + ); }); }); }); diff --git a/cypress/support/storage.ts b/cypress/support/storage.ts new file mode 100644 index 0000000000..d93e118835 --- /dev/null +++ b/cypress/support/storage.ts @@ -0,0 +1,58 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export class MockStorage implements Storage { + private data: Record = {}; + private keys: string[] = []; + public length = 0; + + constructor() {} + + public setItem(k: string, v: string) { + this.data[k] = v; + this.recalc(); + } + + public getItem(k: string): string | null { + return this.data[k] || null; + } + + public removeItem(k: string) { + delete this.data[k]; + this.recalc(); + } + + public clear() { + this.data = {}; + this.recalc(); + } + + public key(index: number): string { + return this.keys[index]; + } + + private recalc() { + const keys = []; + for (const k in this.data) { + if (!this.data.hasOwnProperty(k)) { + continue; + } + keys.push(k); + } + this.keys = keys; + this.length = keys.length; + } +}