You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-29 16:43:09 +03:00
Supporting infrastructure for educated decisions on when to upgrade rooms
Part of https://github.com/vector-im/riot-web/issues/8251
This commit is contained in:
@@ -59,6 +59,7 @@ Promise.config({warnings: false});
|
|||||||
|
|
||||||
const SCROLLBACK_DELAY_MS = 3000;
|
const SCROLLBACK_DELAY_MS = 3000;
|
||||||
const CRYPTO_ENABLED = isCryptoAvailable();
|
const CRYPTO_ENABLED = isCryptoAvailable();
|
||||||
|
const CAPABILITIES_CACHE_MS = 21600000; // 6 hours - an arbitrary value
|
||||||
|
|
||||||
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
function keysFromRecoverySession(sessions, decryptionKey, roomId) {
|
||||||
const keys = [];
|
const keys = [];
|
||||||
@@ -225,6 +226,8 @@ function MatrixClient(opts) {
|
|||||||
this._pushProcessor = new PushProcessor(this);
|
this._pushProcessor = new PushProcessor(this);
|
||||||
|
|
||||||
this._serverSupportsLazyLoading = null;
|
this._serverSupportsLazyLoading = null;
|
||||||
|
|
||||||
|
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
|
||||||
}
|
}
|
||||||
utils.inherits(MatrixClient, EventEmitter);
|
utils.inherits(MatrixClient, EventEmitter);
|
||||||
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
|
utils.extend(MatrixClient.prototype, MatrixBaseApis.prototype);
|
||||||
@@ -392,6 +395,32 @@ MatrixClient.prototype.setNotifTimelineSet = function(notifTimelineSet) {
|
|||||||
this._notifTimelineSet = notifTimelineSet;
|
this._notifTimelineSet = notifTimelineSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the capabilities of the homeserver. Always returns an object of
|
||||||
|
* capability keys and their options, which may be empty.
|
||||||
|
* @return {module:client.Promise} Resolves to the capabilities of the homeserver
|
||||||
|
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||||
|
*/
|
||||||
|
MatrixClient.prototype.getCapabilities = function() {
|
||||||
|
if (this._cachedCapabilities) {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (now - this._cachedCapabilities.lastUpdated <= CAPABILITIES_CACHE_MS) {
|
||||||
|
return Promise.resolve(this._cachedCapabilities.capabilities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._http.authedRequest(
|
||||||
|
undefined, "GET", "/capabilities",
|
||||||
|
).then((r) => {
|
||||||
|
if (!r) r = {};
|
||||||
|
const capabilities = r["capabilities"] || {};
|
||||||
|
this._cachedCapabilities = {
|
||||||
|
capabilities: capabilities,
|
||||||
|
lastUpdated: new Date().getTime(),
|
||||||
|
};
|
||||||
|
return capabilities;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Crypto bits
|
// Crypto bits
|
||||||
// ===========
|
// ===========
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const EventTimelineSet = require("./event-timeline-set");
|
|||||||
|
|
||||||
import ReEmitter from '../ReEmitter';
|
import ReEmitter from '../ReEmitter';
|
||||||
|
|
||||||
const LATEST_ROOM_VERSION = '1';
|
const KNOWN_SAFE_ROOM_VERSION = '1';
|
||||||
const SAFE_ROOM_VERSIONS = ['1', '2'];
|
const SAFE_ROOM_VERSIONS = ['1', '2'];
|
||||||
|
|
||||||
function synthesizeReceipt(userId, event, receiptType) {
|
function synthesizeReceipt(userId, event, receiptType) {
|
||||||
@@ -212,19 +212,76 @@ Room.prototype.getVersion = function() {
|
|||||||
* Determines whether this room needs to be upgraded to a new version
|
* Determines whether this room needs to be upgraded to a new version
|
||||||
* @returns {string?} What version the room should be upgraded to, or null if
|
* @returns {string?} What version the room should be upgraded to, or null if
|
||||||
* the room does not require upgrading at this time.
|
* the room does not require upgrading at this time.
|
||||||
|
* @deprecated Use #getRecommendedVersion() instead
|
||||||
*/
|
*/
|
||||||
Room.prototype.shouldUpgradeToVersion = function() {
|
Room.prototype.shouldUpgradeToVersion = function() {
|
||||||
// This almost certainly won't be the way this actually works - this
|
// TODO: Remove this function.
|
||||||
// is essentially a stub method.
|
// This makes assumptions about which versions are safe, and can easily
|
||||||
// Something like https://github.com/matrix-org/matrix-doc/pull/1804
|
// be wrong. Instead, people are encouraged to use getRecommendedVersion
|
||||||
// would solve this problem for us.
|
// which determines a safer value. This function doesn't use that function
|
||||||
|
// because this is not async-capable, and to avoid breaking the contract
|
||||||
|
// we're deprecating this.
|
||||||
|
|
||||||
if (!SAFE_ROOM_VERSIONS.includes(this.getVersion())) {
|
if (!SAFE_ROOM_VERSIONS.includes(this.getVersion())) {
|
||||||
return LATEST_ROOM_VERSION;
|
return KNOWN_SAFE_ROOM_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the recommended room version for the room. This returns an
|
||||||
|
* object with 3 properties: <code>version</code> as the new version the
|
||||||
|
* room should be upgraded to (may be the same as the current version);
|
||||||
|
* <code>needsUpgrade</code> to indicate if the room actually can be
|
||||||
|
* upgraded (ie: does the current version not match?); and <code>urgent</code>
|
||||||
|
* to indicate if the new version patches a vulnerability in a previous
|
||||||
|
* version.
|
||||||
|
* @returns {Promise<{version: string, needsUpgrade: bool, urgent: bool}>}
|
||||||
|
* Resolves to the version the room should be upgraded to.
|
||||||
|
*/
|
||||||
|
Room.prototype.getRecommendedVersion = async function() {
|
||||||
|
const capabilities = await this._client.getCapabilities();
|
||||||
|
let versionCap = capabilities["m.room_versions"];
|
||||||
|
if (!versionCap) {
|
||||||
|
versionCap = {
|
||||||
|
default: KNOWN_SAFE_ROOM_VERSION,
|
||||||
|
available: {},
|
||||||
|
};
|
||||||
|
for (const safeVer of SAFE_ROOM_VERSIONS) {
|
||||||
|
versionCap.available[safeVer] = "stable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentVersion = this.getVersion();
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
version: currentVersion,
|
||||||
|
needsUpgrade: false,
|
||||||
|
urgent: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the room is on the default version then nothing needs to change
|
||||||
|
if (currentVersion === versionCap.default) return Promise.resolve(result);
|
||||||
|
|
||||||
|
const stableVersions = Object.keys(versionCap.available)
|
||||||
|
.filter((v) => versionCap.available[v] === 'stable');
|
||||||
|
|
||||||
|
// Check if the room is on an unstable version. We determine urgency based
|
||||||
|
// off the version being in the Matrix spec namespace or not (if the version
|
||||||
|
// is in the current namespace and unstable, the room is probably vulnerable).
|
||||||
|
if (!stableVersions.includes(currentVersion)) {
|
||||||
|
result.version = versionCap.default;
|
||||||
|
result.needsUpgrade = true;
|
||||||
|
result.urgent = !!this.getVersion().match(/^[0-9]+[0-9.]*$/g);
|
||||||
|
return Promise.resolve(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The room is on a stable, but non-default, version by this point.
|
||||||
|
// No upgrade needed.
|
||||||
|
return Promise.resolve(result);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether the given user is permitted to perform a room upgrade
|
* Determines whether the given user is permitted to perform a room upgrade
|
||||||
* @param {String} userId The ID of the user to test against
|
* @param {String} userId The ID of the user to test against
|
||||||
|
|||||||
Reference in New Issue
Block a user