1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-06-10 02:21:19 +03:00

Element R: emit events when devices have changed (#4019)

* emit events when Rust crypto wasm tells us devices have changed

* lint

* add missing stub function

* apply workaround for queueMicrotask
This commit is contained in:
Hubert Chathi 2024-01-31 09:31:28 -05:00 committed by GitHub
parent f81036346f
commit d178fbf9cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 79 additions and 1 deletions

View File

@ -94,6 +94,7 @@ describe("initRustCrypto", () => {
getSecretsFromInbox: jest.fn().mockResolvedValue([]),
deleteSecretsFromInbox: jest.fn(),
registerReceiveSecretCallback: jest.fn(),
registerDevicesUpdatedCallback: jest.fn(),
outgoingRequests: jest.fn(),
isBackupEnabled: jest.fn().mockResolvedValue(false),
verifyBackup: jest.fn().mockResolvedValue({ trusted: jest.fn().mockReturnValue(false) }),
@ -1132,6 +1133,33 @@ describe("RustCrypto", () => {
rustCrypto.stop();
});
it("should emit events on device changes", async () => {
jest.useFakeTimers({ doNotFake: ["queueMicrotask"] });
fetchMock.post("path:/_matrix/client/v3/keys/upload", { one_time_key_counts: {} });
fetchMock.post("path:/_matrix/client/v3/keys/query", {
device_keys: {
[testData.TEST_USER_ID]: {
[testData.TEST_DEVICE_ID]: testData.SIGNED_TEST_DEVICE_DATA,
},
},
});
const rustCrypto = await makeTestRustCrypto(makeMatrixHttpApi(), testData.TEST_USER_ID);
const willUpdateCallback = jest.fn();
rustCrypto.on(CryptoEvent.WillUpdateDevices, willUpdateCallback);
const devicesUpdatedCallback = jest.fn();
rustCrypto.on(CryptoEvent.DevicesUpdated, devicesUpdatedCallback);
rustCrypto.onSyncCompleted({});
// wait for the devices to be updated
await rustCrypto.getUserDeviceInfo([testData.TEST_USER_ID]);
expect(willUpdateCallback).toHaveBeenCalledWith([testData.TEST_USER_ID], false);
expect(devicesUpdatedCallback).toHaveBeenCalledWith([testData.TEST_USER_ID], false);
rustCrypto.stop();
});
describe("requestDeviceVerification", () => {
it("throws an error if the device is unknown", async () => {
const rustCrypto = await makeTestRustCrypto();

View File

@ -2360,6 +2360,9 @@ export class MatrixClient extends TypedEventEmitter<EmittedEvents, ClientEventHa
CryptoEvent.KeyBackupSessionsRemaining,
CryptoEvent.KeyBackupFailed,
CryptoEvent.KeyBackupDecryptionKeyCached,
CryptoEvent.KeysChanged,
CryptoEvent.DevicesUpdated,
CryptoEvent.WillUpdateDevices,
]);
}

View File

@ -158,6 +158,7 @@ async function initOlmMachine(
await olmMachine.registerUserIdentityUpdatedCallback((userId: RustSdkCryptoJs.UserId) =>
rustCrypto.onUserIdentityUpdated(userId),
);
await olmMachine.registerDevicesUpdatedCallback((userIds: string[]) => rustCrypto.onDevicesUpdated(userIds));
// Check if there are any key backup secrets pending processing. There may be multiple secrets to process if several devices have gossiped them.
// The `registerReceiveSecretCallback` function will only be triggered for new secrets. If the client is restarted before processing them, the secrets will need to be manually handled.

View File

@ -1441,7 +1441,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
* Callback for `OlmMachine.registerUserIdentityUpdatedCallback`
*
* Called by the rust-sdk whenever there is an update to any user's cross-signing status. We re-check their trust
* status and emit a `UserTrustStatusChanged` event.
* status and emit a `UserTrustStatusChanged` event, as well as a `KeysChanged` if it is our own identity that changed.
*
* @param userId - the user with the updated identity
*/
@ -1452,10 +1452,26 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
// If our own user identity has changed, we may now trust the key backup where we did not before.
// So, re-check the key backup status and enable it if available.
if (userId.toString() === this.userId) {
this.emit(CryptoEvent.KeysChanged, {});
await this.checkKeyBackupAndEnable();
}
}
/**
* Callback for `OlmMachine.registerDevicesUpdatedCallback`
*
* Called when users' devices have updated. Emits `WillUpdateDevices` and `DevicesUpdated`. In the JavaScript
* crypto backend, these events are called at separate times, with `WillUpdateDevices` being emitted just before
* the devices are saved, and `DevicesUpdated` being emitted just after. But the OlmMachine only gives us
* one event, so we emit both events here.
*
* @param userIds - an array of user IDs of users whose devices have updated.
*/
public async onDevicesUpdated(userIds: string[]): Promise<void> {
this.emit(CryptoEvent.WillUpdateDevices, userIds, false);
this.emit(CryptoEvent.DevicesUpdated, userIds, false);
}
/**
* Handles secret received from the rust secret inbox.
*
@ -1828,6 +1844,9 @@ function rustEncryptionInfoToJsEncryptionInfo(
type RustCryptoEvents =
| CryptoEvent.VerificationRequestReceived
| CryptoEvent.UserTrustStatusChanged
| CryptoEvent.KeysChanged
| CryptoEvent.WillUpdateDevices
| CryptoEvent.DevicesUpdated
| RustBackupCryptoEvents;
type RustCryptoEventMap = {
@ -1842,4 +1861,31 @@ type RustCryptoEventMap = {
[CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserVerificationStatus) => void;
[CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void;
/**
* Fires when the user's cross-signing keys have changed or cross-signing
* has been enabled/disabled. The client can use getStoredCrossSigningForUser
* with the user ID of the logged in user to check if cross-signing is
* enabled on the account. If enabled, it can test whether the current key
* is trusted using with checkUserTrust with the user ID of the logged
* in user. The checkOwnCrossSigningTrust function may be used to reconcile
* the trust in the account key.
*
* The cross-signing API is currently UNSTABLE and may change without notice.
* @experimental
*/
[CryptoEvent.KeysChanged]: (data: {}) => void;
/**
* Fires whenever the stored devices for a user will be updated
* @param users - A list of user IDs that will be updated
* @param initialFetch - If true, the store is empty (apart
* from our own device) and is being seeded.
*/
[CryptoEvent.WillUpdateDevices]: (users: string[], initialFetch: boolean) => void;
/**
* Fires whenever the stored devices for a user have changed
* @param users - A list of user IDs that were updated
* @param initialFetch - If true, the store was empty (apart
* from our own device) and has been seeded.
*/
[CryptoEvent.DevicesUpdated]: (users: string[], initialFetch: boolean) => void;
} & RustBackupCryptoEventMap;