1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

Revert "Revert "Lazy loading: don't block on setting up room crypto""

This reverts commit 5cf2ebea4f.
This commit is contained in:
Bruno Windels
2018-08-24 10:55:59 +02:00
parent 78b08bfef2
commit 21e0c79f7d
5 changed files with 102 additions and 56 deletions

View File

@@ -1318,6 +1318,9 @@ describe("Room", function() {
// events should already be MatrixEvents
return function(event) {return event;};
},
isRoomEncrypted: function() {
return false;
},
_http: {
serverResponse,
authedRequest: function() {

View File

@@ -71,6 +71,9 @@ export default class RoomList {
}
async setRoomEncryption(roomId, roomInfo) {
// important that this happens before calling into the store
// as it prevents the Crypto::setRoomEncryption for calling
// this twice for consecutive m.room.encryption events
this._roomEncryption[roomId] = roomInfo;
await this._cryptoStore.doTxn(
'readwrite', [IndexedDBCryptoStore.STORE_ROOMS], (txn) => {

View File

@@ -106,6 +106,10 @@ function Crypto(baseApis, sessionStore, userId, deviceId,
this._receivedRoomKeyRequestCancellations = [];
// true if we are currently processing received room key requests
this._processingRoomKeyRequests = false;
// track if an initial tracking of all the room members
// has happened for a given room. This is delayed
// to avoid loading room members as long as possible.
this._roomDeviceTrackingState = {};
}
utils.inherits(Crypto, EventEmitter);
@@ -612,9 +616,6 @@ Crypto.prototype.getEventSenderDeviceInfo = function(event) {
* @param {string} roomId The room ID to enable encryption in.
*
* @param {object} config The encryption config for the room.
*
* @param {boolean=} inhibitDeviceQuery true to suppress device list query for
* users in the room (for now)
*/
Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDeviceQuery) {
// if state is being replayed from storage, we might already have a configuration
@@ -670,25 +671,42 @@ Crypto.prototype.setRoomEncryption = async function(roomId, config, inhibitDevic
if (storeConfigPromise) {
await storeConfigPromise;
}
// make sure we are tracking the device lists for all users in this room.
console.log("Enabling encryption in " + roomId + "; " +
"starting to track device lists for all users therein");
const room = this._clientStore.getRoom(roomId);
if (!room) {
throw new Error(`Unable to enable encryption in unknown room ${roomId}`);
}
const members = await room.getEncryptionTargetMembers();
members.forEach((m) => {
this._deviceList.startTrackingDeviceList(m.userId);
});
if (!inhibitDeviceQuery) {
this._deviceList.refreshOutdatedDeviceLists();
}
console.log("Enabling encryption in " + roomId);
};
/**
* Make sure we are tracking the device lists for all users in this room.
*
* @param {string} roomId The room ID to start tracking devices in.
* @returns {Promise} when all devices for the room have been fetched and marked to track
*/
Crypto.prototype.trackRoomDevices = function(roomId) {
const trackAndRefresh = async () => {
// not an encrypted room
if (!this._roomEncryptors[roomId]) {
return;
}
const room = this._clientStore.getRoom(roomId);
if (!room) {
throw new Error(`Unable to start tracking devices in unknown room ${roomId}`);
}
console.log(`Starting to track devices for room ${roomId} ...`);
const members = await room.getEncryptionTargetMembers();
members.forEach((m) => {
this._deviceList.startTrackingDeviceList(m.userId);
});
return this._deviceList.refreshOutdatedDeviceLists();
};
let promise = this._roomDeviceTrackingState[roomId];
if (!promise) {
promise = trackAndRefresh();
this._roomDeviceTrackingState[roomId] = promise;
}
return promise;
};
/**
* @typedef {Object} module:crypto~OlmSessionResult
* @property {module:crypto/deviceinfo} device device info
@@ -779,7 +797,7 @@ Crypto.prototype.importRoomKeys = function(keys) {
},
);
};
/* eslint-disable valid-jsdoc */ //https://github.com/eslint/eslint/issues/7307
/**
* Encrypt an event according to the configuration of the room.
*
@@ -790,7 +808,8 @@ Crypto.prototype.importRoomKeys = function(keys) {
* @return {module:client.Promise?} Promise which resolves when the event has been
* encrypted, or null if nothing was needed
*/
Crypto.prototype.encryptEvent = function(event, room) {
/* eslint-enable valid-jsdoc */
Crypto.prototype.encryptEvent = async function(event, room) {
if (!room) {
throw new Error("Cannot send encrypted messages in unknown rooms");
}
@@ -808,6 +827,12 @@ Crypto.prototype.encryptEvent = function(event, room) {
);
}
if (!this._roomDeviceTrackingState[roomId]) {
this.trackRoomDevices(roomId);
}
// wait for all the room devices to be loaded
await this._roomDeviceTrackingState[roomId];
let content = event.getContent();
// If event has an m.relates_to then we need
// to put this on the wrapping event instead
@@ -818,20 +843,19 @@ Crypto.prototype.encryptEvent = function(event, room) {
delete content['m.relates_to'];
}
return alg.encryptMessage(
room, event.getType(), content,
).then((encryptedContent) => {
if (mRelatesTo) {
encryptedContent['m.relates_to'] = mRelatesTo;
}
const encryptedContent = await alg.encryptMessage(
room, event.getType(), content);
event.makeEncrypted(
"m.room.encrypted",
encryptedContent,
this._olmDevice.deviceCurve25519Key,
this._olmDevice.deviceEd25519Key,
);
});
if (mRelatesTo) {
encryptedContent['m.relates_to'] = mRelatesTo;
}
event.makeEncrypted(
"m.room.encrypted",
encryptedContent,
this._olmDevice.deviceCurve25519Key,
this._olmDevice.deviceEd25519Key,
);
};
/**
@@ -924,9 +948,7 @@ Crypto.prototype.onCryptoEvent = async function(event) {
const content = event.getContent();
try {
// inhibit the device list refresh for now - it will happen once we've
// finished processing the sync, in onSyncCompleted.
await this.setRoomEncryption(roomId, content, true);
await this.setRoomEncryption(roomId, content);
} catch (e) {
console.error("Error configuring encryption in room " + roomId +
":", e);
@@ -946,6 +968,7 @@ Crypto.prototype.onSyncWillProcess = async function(syncData) {
// at which point we'll start tracking all the users of that room.
console.log("Initial sync performed - resetting device tracking state");
this._deviceList.stopTrackingAllDeviceLists();
this._roomDeviceTrackingState = {};
}
};
@@ -991,11 +1014,12 @@ Crypto.prototype._evalDeviceListChanges = async function(deviceLists) {
});
}
if (deviceLists.left && Array.isArray(deviceLists.left)) {
if (deviceLists.left && Array.isArray(deviceLists.left) &&
deviceLists.left.length) {
// Check we really don't share any rooms with these users
// any more: the server isn't required to give us the
// exact correct set.
const e2eUserIds = new Set(await this._getE2eUsers());
const e2eUserIds = new Set(await this._getTrackedE2eUsers());
deviceLists.left.forEach((u) => {
if (!e2eUserIds.has(u)) {
@@ -1007,12 +1031,13 @@ Crypto.prototype._evalDeviceListChanges = async function(deviceLists) {
/**
* Get a list of all the IDs of users we share an e2e room with
* for which we are tracking devices already
*
* @returns {string[]} List of user IDs
*/
Crypto.prototype._getE2eUsers = async function() {
Crypto.prototype._getTrackedE2eUsers = async function() {
const e2eUserIds = [];
for (const room of this._getE2eRooms()) {
for (const room of this._getTrackedE2eRooms()) {
const members = await room.getEncryptionTargetMembers();
for (const member of members) {
e2eUserIds.push(member.userId);
@@ -1022,17 +1047,21 @@ Crypto.prototype._getE2eUsers = async function() {
};
/**
* Get a list of the e2e-enabled rooms we are members of
* Get a list of the e2e-enabled rooms we are members of,
* and for which we are already tracking the devices
*
* @returns {module:models.Room[]}
*/
Crypto.prototype._getE2eRooms = function() {
Crypto.prototype._getTrackedE2eRooms = function() {
return this._clientStore.getRooms().filter((room) => {
// check for rooms with encryption enabled
const alg = this._roomEncryptors[room.roomId];
if (!alg) {
return false;
}
if (!this._roomDeviceTrackingState[room.roomId]) {
return false;
}
// ignore any rooms which we have left
const myMembership = room.getMyMembership();
@@ -1101,15 +1130,20 @@ Crypto.prototype._onRoomMembership = function(event, member, oldMembership) {
// not encrypting in this room
return;
}
if (member.membership == 'join') {
console.log('Join event for ' + member.userId + ' in ' + roomId);
// make sure we are tracking the deviceList for this user
this._deviceList.startTrackingDeviceList(member.userId);
} else if (member.membership == 'invite' &&
this._clientStore.getRoom(roomId).shouldEncryptForInvitedMembers()) {
console.log('Invite event for ' + member.userId + ' in ' + roomId);
this._deviceList.startTrackingDeviceList(member.userId);
// only mark users in this room as tracked if we already started tracking in this room
// this way we don't start device queries after sync on behalf of this room which we won't use
// the result of anyway, as we'll need to do a query again once all the members are fetched
// by calling _trackRoomDevices
if (this._roomDeviceTrackingState[roomId]) {
if (member.membership == 'join') {
console.log('Join event for ' + member.userId + ' in ' + roomId);
// make sure we are tracking the deviceList for this user
this._deviceList.startTrackingDeviceList(member.userId);
} else if (member.membership == 'invite' &&
this._clientStore.getRoom(roomId).shouldEncryptForInvitedMembers()) {
console.log('Invite event for ' + member.userId + ' in ' + roomId);
this._deviceList.startTrackingDeviceList(member.userId);
}
}
alg.onRoomMembership(event, member, oldMembership);

View File

@@ -368,6 +368,11 @@ Room.prototype.loadMembersIfNeeded = function() {
});
this._membersPromise = promise;
// now the members are loaded, start to track the e2e devices if needed
if (this._client.isRoomEncrypted(this.roomId)) {
this._client._crypto.trackRoomDevices(this.roomId);
}
return this._membersPromise;
};

View File

@@ -1085,15 +1085,16 @@ SyncApi.prototype._processSyncResponse = async function(
self._processEventsForNotifs(room, timelineEvents);
async function processRoomEvent(e) {
function processRoomEvent(e) {
client.emit("event", e);
if (e.isState() && e.getType() == "m.room.encryption" && self.opts.crypto) {
await self.opts.crypto.onCryptoEvent(e);
self.opts.crypto.onCryptoEvent(e);
}
}
await Promise.mapSeries(stateEvents, processRoomEvent);
await Promise.mapSeries(timelineEvents, processRoomEvent);
stateEvents.forEach(processRoomEvent);
timelineEvents.forEach(processRoomEvent);
ephemeralEvents.forEach(function(e) {
client.emit("event", e);
});