Skip to content

Commit

Permalink
Add support for stable name for MSC4115 (#4232)
Browse files Browse the repository at this point in the history
* add support for stable name for MSC4115

* fix types issues

* prettier

* actually, it still returns `undefined`
  • Loading branch information
uhoreg authored Jun 7, 2024
1 parent fa5f2d3 commit f95954c
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 9 deletions.
68 changes: 68 additions & 0 deletions spec/integ/crypto/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,27 @@ 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 () => {
Expand All @@ -654,6 +675,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 () => {
Expand All @@ -676,6 +721,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 () => {
Expand Down
4 changes: 2 additions & 2 deletions src/@types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 2 additions & 7 deletions src/models/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ export interface IUnsigned {
"invite_room_state"?: StrippedState[];
"m.relations"?: Record<RelationType | string, any>; // No common pattern for aggregated relations
[UNSIGNED_THREAD_ID_FIELD.name]?: string;
[UNSIGNED_MEMBERSHIP_FIELD.name]?: Membership | string;
}

export interface IThreadBundledRelationship {
Expand Down Expand Up @@ -717,13 +716,9 @@ export class MatrixEvent extends TypedEventEmitter<MatrixEventEmittedEvents, Mat
* @returns The user's room membership, or `undefined` if the server does
* not report it.
*/
public getMembershipAtEvent(): Membership | string | undefined {
public getMembershipAtEvent(): Optional<Membership | string> {
const unsigned = this.getUnsigned();
if (typeof unsigned[UNSIGNED_MEMBERSHIP_FIELD.name] === "string") {
return unsigned[UNSIGNED_MEMBERSHIP_FIELD.name];
} else {
return undefined;
}
return UNSIGNED_MEMBERSHIP_FIELD.findIn<Membership | string>(unsigned);
}

/**
Expand Down

0 comments on commit f95954c

Please sign in to comment.