From 338467a56cd6cba80314a640cb66408372871793 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 6 Jun 2024 15:37:49 -0400 Subject: [PATCH 1/4] add support for stable name for MSC4115 --- spec/integ/crypto/crypto.spec.ts | 63 ++++++++++++++++++++++++++++++++ src/@types/event.ts | 4 +- src/models/event.ts | 4 ++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index 50b5f4dea05..ad5a251c26e 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -630,6 +630,22 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED); }); + newBackendOnly("fails with NOT_JOINED if user is not member of room (MSC4115 unstable prefix)", async () => { + fetchMock.get("path:/_matrix/client/v3/room_keys/version", { + status: 404, + body: { errcode: "M_NOT_FOUND", error: "No current backup version." }, + }); + expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); + await startClientAndAwaitFirstSync(); + + const ev = await sendEventAndAwaitDecryption({ + unsigned: { + [UNSIGNED_MEMBERSHIP_FIELD.altName]: "leave", + }, + }); + expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED); + }); + newBackendOnly( "fails with another error when the server reports user was a member of the room", async () => { @@ -654,6 +670,30 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, }, ); + newBackendOnly( + "fails with another error when the server reports user was a member of the room (MSC4115 unstable prefix)", + async () => { + // This tests that when the server reports that the user + // was invited at the time the event was sent, then we + // don't get a HISTORICAL_MESSAGE_USER_NOT_JOINED error, + // and instead get some other error, since the user should + // have gotten the key for the event. + fetchMock.get("path:/_matrix/client/v3/room_keys/version", { + status: 404, + body: { errcode: "M_NOT_FOUND", error: "No current backup version." }, + }); + expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); + await startClientAndAwaitFirstSync(); + + const ev = await sendEventAndAwaitDecryption({ + unsigned: { + [UNSIGNED_MEMBERSHIP_FIELD.altName]: "invite", + }, + }); + expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP); + }, + ); + newBackendOnly( "fails with another error when the server reports user was a member of the room", async () => { @@ -676,6 +716,29 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP); }, ); + + newBackendOnly( + "fails with another error when the server reports user was a member of the room (MSC4115 unstable prefix)", + async () => { + // This tests that when the server reports the user's + // membership, and reports that the user was joined, then we + // don't get a HISTORICAL_MESSAGE_USER_NOT_JOINED error, and + // instead get some other error. + fetchMock.get("path:/_matrix/client/v3/room_keys/version", { + status: 404, + body: { errcode: "M_NOT_FOUND", error: "No current backup version." }, + }); + expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); + await startClientAndAwaitFirstSync(); + + const ev = await sendEventAndAwaitDecryption({ + unsigned: { + [UNSIGNED_MEMBERSHIP_FIELD.altName]: "join", + }, + }); + expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP); + }, + ); }); it("Decryption fails with Unable to decrypt for other errors", async () => { diff --git a/src/@types/event.ts b/src/@types/event.ts index 5d8a26c9b23..44ef4c1434c 100644 --- a/src/@types/event.ts +++ b/src/@types/event.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { UnstableValue } from "../NamespacedValue"; +import { NamespacedValue, UnstableValue } from "../NamespacedValue"; import { PolicyRuleEventContent, RoomAvatarEventContent, @@ -302,7 +302,7 @@ export const UNSIGNED_THREAD_ID_FIELD = new UnstableValue("thread_id", "org.matr * * @experimental */ -export const UNSIGNED_MEMBERSHIP_FIELD = new UnstableValue("membership", "io.element.msc4115.membership"); +export const UNSIGNED_MEMBERSHIP_FIELD = new NamespacedValue("membership", "io.element.msc4115.membership"); /** * Mapped type from event type to content type for all specified non-state room events. diff --git a/src/models/event.ts b/src/models/event.ts index 7b9365aad50..d9abf96ee8b 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -78,6 +78,7 @@ export interface IUnsigned { "m.relations"?: Record; // No common pattern for aggregated relations [UNSIGNED_THREAD_ID_FIELD.name]?: string; [UNSIGNED_MEMBERSHIP_FIELD.name]?: Membership | string; + [UNSIGNED_MEMBERSHIP_FIELD.altName]?: Membership | string; } export interface IThreadBundledRelationship { @@ -719,8 +720,11 @@ export class MatrixEvent extends TypedEventEmitter Date: Thu, 6 Jun 2024 17:32:55 -0400 Subject: [PATCH 2/4] fix types issues --- spec/integ/crypto/crypto.spec.ts | 6 +++--- src/models/event.ts | 15 +++------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index ad5a251c26e..5e98aac625a 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -640,7 +640,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, const ev = await sendEventAndAwaitDecryption({ unsigned: { - [UNSIGNED_MEMBERSHIP_FIELD.altName]: "leave", + [UNSIGNED_MEMBERSHIP_FIELD.altName!]: "leave", }, }); expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED); @@ -687,7 +687,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, const ev = await sendEventAndAwaitDecryption({ unsigned: { - [UNSIGNED_MEMBERSHIP_FIELD.altName]: "invite", + [UNSIGNED_MEMBERSHIP_FIELD.altName!]: "invite", }, }); expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP); @@ -733,7 +733,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, const ev = await sendEventAndAwaitDecryption({ unsigned: { - [UNSIGNED_MEMBERSHIP_FIELD.altName]: "join", + [UNSIGNED_MEMBERSHIP_FIELD.altName!]: "join", }, }); expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP); diff --git a/src/models/event.ts b/src/models/event.ts index d9abf96ee8b..80894390066 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -77,8 +77,6 @@ export interface IUnsigned { "invite_room_state"?: StrippedState[]; "m.relations"?: Record; // No common pattern for aggregated relations [UNSIGNED_THREAD_ID_FIELD.name]?: string; - [UNSIGNED_MEMBERSHIP_FIELD.name]?: Membership | string; - [UNSIGNED_MEMBERSHIP_FIELD.altName]?: Membership | string; } export interface IThreadBundledRelationship { @@ -715,19 +713,12 @@ export class MatrixEvent extends TypedEventEmitter { const unsigned = this.getUnsigned(); - // check the stable name first, then check the unstable name - if (typeof unsigned[UNSIGNED_MEMBERSHIP_FIELD.name] === "string") { - return unsigned[UNSIGNED_MEMBERSHIP_FIELD.name]; - } else if (typeof unsigned[UNSIGNED_MEMBERSHIP_FIELD.altName] === "string") { - return unsigned[UNSIGNED_MEMBERSHIP_FIELD.altName]; - } else { - return undefined; - } + return UNSIGNED_MEMBERSHIP_FIELD.findIn(unsigned); } /** From 550a28a76ef356eff794b35742c82b1d9053910d Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 6 Jun 2024 17:35:52 -0400 Subject: [PATCH 3/4] prettier --- spec/integ/crypto/crypto.spec.ts | 33 ++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index 5e98aac625a..fd3b2778cfd 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -630,21 +630,26 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED); }); - newBackendOnly("fails with NOT_JOINED if user is not member of room (MSC4115 unstable prefix)", async () => { - fetchMock.get("path:/_matrix/client/v3/room_keys/version", { - status: 404, - body: { errcode: "M_NOT_FOUND", error: "No current backup version." }, - }); - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); + newBackendOnly( + "fails with NOT_JOINED if user is not member of room (MSC4115 unstable prefix)", + async () => { + fetchMock.get("path:/_matrix/client/v3/room_keys/version", { + status: 404, + body: { errcode: "M_NOT_FOUND", error: "No current backup version." }, + }); + expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); + await startClientAndAwaitFirstSync(); - const ev = await sendEventAndAwaitDecryption({ - unsigned: { - [UNSIGNED_MEMBERSHIP_FIELD.altName!]: "leave", - }, - }); - expect(ev.decryptionFailureReason).toEqual(DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED); - }); + const ev = await sendEventAndAwaitDecryption({ + unsigned: { + [UNSIGNED_MEMBERSHIP_FIELD.altName!]: "leave", + }, + }); + expect(ev.decryptionFailureReason).toEqual( + DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED, + ); + }, + ); newBackendOnly( "fails with another error when the server reports user was a member of the room", From 75e0e6146c54bf0a591c3d238248ae57c8a49583 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Thu, 6 Jun 2024 17:43:10 -0400 Subject: [PATCH 4/4] actually, it still returns `undefined` --- src/models/event.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/event.ts b/src/models/event.ts index 80894390066..e78a2f5b233 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -713,7 +713,7 @@ export class MatrixEvent extends TypedEventEmitter {