Compare commits

...

39 Commits

Author SHA1 Message Date
RiotRobot bc5b587651 v7.0.0 2020-06-23 14:42:09 +01:00
RiotRobot 8083031029 Prepare changelog for v7.0.0 2020-06-23 14:42:09 +01:00
RiotRobot c3283a7297 v7.0.0-rc.1 2020-06-17 21:33:36 +01:00
RiotRobot f423164a1c Prepare changelog for v7.0.0-rc.1 2020-06-17 21:33:35 +01:00
RiotRobot 1c194e8163 Merge branch 'master' into develop 2020-06-16 11:04:39 +01:00
Michael Telatynski aa2d0d9a08 Merge pull request #1404 from matrix-org/t3chguy/push-rules-device
Remove support for unspecced device-specific push rules
2020-06-16 10:26:08 +01:00
Michael Telatynski be05452c70 Fix tests
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-06-14 13:15:28 +01:00
Michael Telatynski 583650cf7d Remove support for unspecced device-specific push rules
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-06-13 14:49:49 +01:00
Michael Telatynski 505915528f Merge pull request #1403 from matrix-org/t3chguy/attemptAuth-existing-session
Use existing session id for fetching flows as to not get a new session
2020-06-12 22:33:26 +01:00
Michael Telatynski ace8a787b4 undo that because {} sux 2020-06-12 20:06:58 +01:00
Michael Telatynski ed2ea9ac8e clean up 2020-06-12 20:00:08 +01:00
Michael Telatynski 8d09a4abe6 Use existing session id for fetching flows as to not get a new session id 2020-06-12 19:50:38 +01:00
J. Ryan Stinnett 1da959ab02 Merge pull request #1400 from matrix-org/jryans/upgrade-deps-2020-06-05
Upgrade deps
2020-06-08 10:58:29 +01:00
J. Ryan Stinnett 997dd9b88a Upgrade deps 2020-06-08 10:13:30 +01:00
RiotRobot ebe66bdd6e Merge branch 'master' into develop 2020-06-05 15:10:44 +01:00
Bruno Windels 013fbb87a7 Merge pull request #1398 from matrix-org/bwindels/backupformatbug
Bring back backup key format migration
2020-06-05 13:25:00 +00:00
Bruno Windels 73764d23dc take into account key can be an object now 2020-06-05 13:38:46 +02:00
Bruno Windels 8f62703bf2 fix type check in migration code 2020-06-05 13:38:15 +02:00
Bruno Windels 6dedae2e4d Revert "remove key backup format migration"
This reverts commit 8d81240c58.
2020-06-05 11:23:04 +02:00
Bruno Windels d32131b2b8 Revert "lint"
This reverts commit 9fe0e1e85f.
2020-06-05 11:20:23 +02:00
Bruno Windels 0a790b2ae3 Merge pull request #1313 from matrix-org/bwindels/throwifcantdecrypt4s
Fix: more informative error message when we cant find a key to decrypt with
2020-06-05 08:30:43 +00:00
RiotRobot ef1d5e3d76 Merge branch 'master' into develop 2020-06-04 15:00:26 +01:00
Michael Telatynski c525a19df5 Merge pull request #1394 from matrix-org/t3chguy/e2eedefault
Add js-sdk mechanism for polling client well-known for config
2020-06-03 10:47:10 +01:00
Michael Telatynski a987a31667 Merge pull request #1388 from matrix-org/dbkr/timeouts
Fix verification request timeouts to match spec
2020-06-03 10:41:15 +01:00
Michael Telatynski 10329c3436 rename _clientWellKnownInterval to _clientWellKnownIntervalID
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-06-03 10:26:46 +01:00
Travis Ralston 29f10bcd44 Merge pull request #1391 from matrix-org/hs/drop-presence-list
Drop presence list methods
2020-06-02 19:50:12 -06:00
Travis Ralston 19fe9b8ac7 Merge pull request #1395 from matrix-org/travis/less-previews
Batch up URL previews to prevent excessive requests
2020-06-02 14:33:04 -06:00
Travis Ralston 76da708352 Call the callback on cached 2020-06-02 14:19:41 -06:00
Travis Ralston 145f01ff2d Batch up URL previews to prevent excessive requests
Cache expiration is still not implemented here.
2020-06-02 13:25:37 -06:00
Michael Telatynski 18b1e00875 Add js-sdk mechanism for polling client well-known for config
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-06-01 22:22:01 +01:00
Will Hunt d021498fa9 Drop presence lists 2020-06-01 11:29:06 +01:00
Michael Telatynski b83aa54661 Test the verification request timeouts of 10m and 2m
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-29 14:49:35 +01:00
Michael Telatynski 429550ca3e fix thing which got missed in the revert
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-29 13:54:08 +01:00
Michael Telatynski 2a6d8c2b1d revert to previous observeOnly behaviour
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-29 13:34:19 +01:00
Michael Telatynski 01c9159830 clean up
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-29 13:17:22 +01:00
Michael Telatynski 3b3ed5159c Implement verification request timeout as per the spec
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2020-05-29 13:10:25 +01:00
David Baker 636661dd45 WIP code for https://github.com/vector-im/riot-web/issues/12430 2020-05-29 09:53:03 +01:00
Bruno Windels cc8e8434ec Update src/crypto/SecretStorage.js
Co-Authored-By: J. Ryan Stinnett <jryans@gmail.com>
2020-04-09 15:58:43 +00:00
Bruno Windels 1b94b3c4de throw something more informative when we cant find a key to decrypt with 2020-04-09 16:00:08 +02:00
8 changed files with 996 additions and 876 deletions
+40
View File
@@ -1,3 +1,43 @@
Changes in [7.0.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v7.0.0) (2020-06-23)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v7.0.0-rc.1...v7.0.0)
* No changes since rc.1
Changes in [7.0.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v7.0.0-rc.1) (2020-06-17)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.2.2...v7.0.0-rc.1)
BREAKING CHANGES
---
* Presence lists were removed from the spec in r0.5.0, and the corresponding methods have now been removed here as well:
* `getPresenceList`
* `inviteToPresenceList`
* `dropFromPresenceList`
All changes
---
* Remove support for unspecced device-specific push rules
[\#1404](https://github.com/matrix-org/matrix-js-sdk/pull/1404)
* Use existing session id for fetching flows as to not get a new session
[\#1403](https://github.com/matrix-org/matrix-js-sdk/pull/1403)
* Upgrade deps
[\#1400](https://github.com/matrix-org/matrix-js-sdk/pull/1400)
* Bring back backup key format migration
[\#1398](https://github.com/matrix-org/matrix-js-sdk/pull/1398)
* Fix: more informative error message when we cant find a key to decrypt with
[\#1313](https://github.com/matrix-org/matrix-js-sdk/pull/1313)
* Add js-sdk mechanism for polling client well-known for config
[\#1394](https://github.com/matrix-org/matrix-js-sdk/pull/1394)
* Fix verification request timeouts to match spec
[\#1388](https://github.com/matrix-org/matrix-js-sdk/pull/1388)
* Drop presence list methods
[\#1391](https://github.com/matrix-org/matrix-js-sdk/pull/1391)
* Batch up URL previews to prevent excessive requests
[\#1395](https://github.com/matrix-org/matrix-js-sdk/pull/1395)
Changes in [6.2.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v6.2.2) (2020-06-16)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v6.2.1...v6.2.2)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "6.2.2",
"version": "7.0.0",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
@@ -119,6 +119,8 @@ async function distributeEvent(ownRequest, theirRequest, event) {
await theirRequest.channel.handleEvent(event, theirRequest, true);
}
jest.useFakeTimers();
describe("verification request unit tests", function() {
beforeAll(function() {
setupWebcrypto();
@@ -246,4 +248,38 @@ describe("verification request unit tests", function() {
expect(bob1Request.done).toBe(true);
expect(bob2Request.done).toBe(true);
});
it("request times out after 10 minutes", async function() {
const alice = makeMockClient("@alice:matrix.tld", "device1");
const bob = makeMockClient("@bob:matrix.tld", "device1");
const aliceRequest = new VerificationRequest(
new InRoomChannel(alice, "!room", bob.getUserId()), new Map(), alice);
await aliceRequest.sendRequest();
const [requestEvent] = alice.popEvents();
await aliceRequest.channel.handleEvent(requestEvent, aliceRequest, true,
true, true);
expect(aliceRequest.cancelled).toBe(false);
expect(aliceRequest._cancellingUserId).toBe(undefined);
jest.advanceTimersByTime(10 * 60 * 1000);
expect(aliceRequest._cancellingUserId).toBe(alice.getUserId());
});
it("request times out 2 minutes after receipt", async function() {
const alice = makeMockClient("@alice:matrix.tld", "device1");
const bob = makeMockClient("@bob:matrix.tld", "device1");
const aliceRequest = new VerificationRequest(
new InRoomChannel(alice, "!room", bob.getUserId()), new Map(), alice);
await aliceRequest.sendRequest();
const [requestEvent] = alice.popEvents();
const bobRequest = new VerificationRequest(
new InRoomChannel(bob, "!room"), new Map(), bob);
await bobRequest.channel.handleEvent(requestEvent, bobRequest, true);
expect(bobRequest.cancelled).toBe(false);
expect(bobRequest._cancellingUserId).toBe(undefined);
jest.advanceTimersByTime(2 * 60 * 1000);
expect(bobRequest._cancellingUserId).toBe(bob.getUserId());
});
});
+48 -51
View File
@@ -54,6 +54,7 @@ import {randomString} from './randomstring';
import {PushProcessor} from "./pushprocessor";
import {encodeBase64, decodeBase64} from "./crypto/olmlib";
import { User } from "./models/user";
import {AutoDiscovery} from "./autodiscovery";
const SCROLLBACK_DELAY_MS = 3000;
export const CRYPTO_ENABLED = isCryptoAvailable();
@@ -329,7 +330,7 @@ export function MatrixClient(opts) {
this._isGuest = false;
this._ongoingScrollbacks = {};
this.timelineSupport = Boolean(opts.timelineSupport);
this.urlPreviewCache = {};
this.urlPreviewCache = {}; // key=preview key, value=Promise for preview (may be an error)
this._notifTimelineSet = null;
this.unstableClientRelationAggregation = !!opts.unstableClientRelationAggregation;
@@ -3001,25 +3002,32 @@ MatrixClient.prototype.setRoomReadMarkers = async function(
* May return synthesized attributes if the URL lacked OG meta.
*/
MatrixClient.prototype.getUrlPreview = function(url, ts, callback) {
// bucket the timestamp to the nearest minute to prevent excessive spam to the server
// Surely 60-second accuracy is enough for anyone.
ts = Math.floor(ts / 60000) * 60000;
const key = ts + "_" + url;
const og = this.urlPreviewCache[key];
if (og) {
return Promise.resolve(og);
// If there's already a request in flight (or we've handled it), return that instead.
const cachedPreview = this.urlPreviewCache[key];
if (cachedPreview) {
if (callback) {
cachedPreview.then(callback).catch(callback);
}
return cachedPreview;
}
const self = this;
return this._http.authedRequest(
const resp = this._http.authedRequest(
callback, "GET", "/preview_url", {
url: url,
ts: ts,
}, undefined, {
prefix: PREFIX_MEDIA_R0,
},
).then(function(response) {
// TODO: expire cache occasionally
self.urlPreviewCache[key] = response;
return response;
});
);
// TODO: Expire the URL preview cache sometimes
this.urlPreviewCache[key] = resp;
return resp;
};
/**
@@ -3521,46 +3529,6 @@ MatrixClient.prototype.setPresence = function(opts, callback) {
);
};
function _presenceList(callback, client, opts, method) {
const path = utils.encodeUri("/presence/list/$userId", {
$userId: client.credentials.userId,
});
return client._http.authedRequest(callback, method, path, undefined, opts);
}
/**
* Retrieve current user presence list.
* @param {module:client.callback} callback Optional.
* @return {Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.getPresenceList = function(callback) {
return _presenceList(callback, this, undefined, "GET");
};
/**
* Add users to the current user presence list.
* @param {module:client.callback} callback Optional.
* @param {string[]} userIds
* @return {Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
*/
MatrixClient.prototype.inviteToPresenceList = function(callback, userIds) {
const opts = {"invite": userIds};
return _presenceList(callback, this, opts, "POST");
};
/**
* Drop users from the current user presence list.
* @param {module:client.callback} callback Optional.
* @param {string[]} userIds
* @return {Promise} Resolves: TODO
* @return {module:http-api.MatrixError} Rejects: with an error response.
**/
MatrixClient.prototype.dropFromPresenceList = function(callback, userIds) {
const opts = {"drop": userIds};
return _presenceList(callback, this, opts, "POST");
};
/**
* Retrieve older messages from the given room and put them in the timeline.
@@ -4762,6 +4730,9 @@ MatrixClient.prototype.deactivateSynapseUser = function(userId) {
* @param {Boolean=} opts.lazyLoadMembers True to not load all membership events during
* initial sync but fetch them when needed by calling `loadOutOfBandMembers`
* This will override the filter option at this moment.
* @param {Number=} opts.clientWellKnownPollPeriod The number of seconds between polls
* to /.well-known/matrix/client, undefined to disable. This should be in the order of hours.
* Default: undefined.
*/
MatrixClient.prototype.startClient = async function(opts) {
if (this.clientRunning) {
@@ -4810,6 +4781,29 @@ MatrixClient.prototype.startClient = async function(opts) {
this._clientOpts = opts;
this._syncApi = new SyncApi(this, opts);
this._syncApi.sync();
if (opts.clientWellKnownPollPeriod !== undefined) {
this._clientWellKnownIntervalID =
setInterval(() => {
this._fetchClientWellKnown();
}, 1000 * opts.clientWellKnownPollPeriod);
this._fetchClientWellKnown();
}
};
MatrixClient.prototype._fetchClientWellKnown = async function() {
try {
this._clientWellKnown = await AutoDiscovery.getRawClientConfig(this.getDomain());
this.emit("WellKnown.client", this._clientWellKnown);
} catch (err) {
logger.error("Failed to get client well-known", err);
this._clientWellKnown = undefined;
this.emit("WellKnown.error", err);
}
};
MatrixClient.prototype.getClientWellKnown = function() {
return this._clientWellKnown;
};
/**
@@ -4851,6 +4845,9 @@ MatrixClient.prototype.stopClient = function() {
this._peekSync.stopPeeking();
}
global.clearTimeout(this._checkTurnServersTimeoutID);
if (this._clientWellKnownIntervalID !== undefined) {
global.clearInterval(this._clientWellKnownIntervalID);
}
};
/**
+5
View File
@@ -292,6 +292,11 @@ export class SecretStorage extends EventEmitter {
}
}
if (Object.keys(keys).length === 0) {
throw new Error(`Could not decrypt ${name} because none of ` +
`the keys it is encrypted with are for a supported algorithm`);
}
let keyId;
let decryption;
try {
@@ -25,15 +25,17 @@ import {
} from "../Error";
import {QRCodeData, SCAN_QR_CODE_METHOD} from "../QRCode";
// the recommended amount of time before a verification request
// should be (automatically) cancelled without user interaction
// and ignored.
const VERIFICATION_REQUEST_TIMEOUT = 10 * 60 * 1000; //10m
// How long after the event's timestamp that the request times out
const TIMEOUT_FROM_EVENT_TS = 10 * 60 * 1000; // 10 minutes
// How long after we receive the event that the request times out
const TIMEOUT_FROM_EVENT_RECEIPT = 2 * 60 * 1000; // 2 minutes
// to avoid almost expired verification notifications
// from showing a notification and almost immediately
// disappearing, also ignore verification requests that
// are this amount of time away from expiring.
const VERIFICATION_REQUEST_MARGIN = 3 * 1000; //3s
const VERIFICATION_REQUEST_MARGIN = 3 * 1000; // 3 seconds
export const EVENT_PREFIX = "m.key.verification.";
@@ -80,6 +82,9 @@ export class VerificationRequest extends EventEmitter {
// cross-signing identity reset between the .ready and .start event
// and signing the wrong key after .start
this._qrCodeData = null;
// The timestamp when we received the request event from the other side
this._requestReceivedAt = null;
}
/**
@@ -165,12 +170,26 @@ export class VerificationRequest extends EventEmitter {
return this._chosenMethod;
}
calculateEventTimeout(event) {
let effectiveExpiresAt = this.channel.getTimestamp(event)
+ TIMEOUT_FROM_EVENT_TS;
if (this._requestReceivedAt && !this.initiatedByMe &&
this.phase <= PHASE_REQUESTED
) {
const expiresAtByReceipt = this._requestReceivedAt
+ TIMEOUT_FROM_EVENT_RECEIPT;
effectiveExpiresAt = Math.min(effectiveExpiresAt, expiresAtByReceipt);
}
return Math.max(0, effectiveExpiresAt - Date.now());
}
/** The current remaining amount of ms before the request should be automatically cancelled */
get timeout() {
const requestEvent = this._getEventByEither(REQUEST_TYPE);
if (requestEvent) {
const elapsed = Date.now() - this.channel.getTimestamp(requestEvent);
return Math.max(0, VERIFICATION_REQUEST_TIMEOUT - elapsed);
return this.calculateEventTimeout(requestEvent);
}
return 0;
}
@@ -735,7 +754,7 @@ export class VerificationRequest extends EventEmitter {
_setupTimeout(phase) {
const shouldTimeout = !this._timeoutTimer && !this.observeOnly &&
phase === PHASE_REQUESTED && this.initiatedByMe;
phase === PHASE_REQUESTED;
if (shouldTimeout) {
this._timeoutTimer = setTimeout(this._cancelOnTimeout, this.timeout);
@@ -754,7 +773,17 @@ export class VerificationRequest extends EventEmitter {
_cancelOnTimeout = () => {
try {
this.cancel({reason: "Other party didn't accept in time", code: "m.timeout"});
if (this.initiatedByMe) {
this.cancel({
reason: "Other party didn't accept in time",
code: "m.timeout",
});
} else {
this.cancel({
reason: "User didn't accept in time",
code: "m.timeout",
});
}
} catch (err) {
logger.error("Error while cancelling verification request", err);
}
@@ -791,16 +820,8 @@ export class VerificationRequest extends EventEmitter {
if (!isLiveEvent) {
this._observeOnly = true;
}
// a timestamp is not provided on all to_device events
const timestamp = this.channel.getTimestamp(event);
if (Number.isFinite(timestamp)) {
const elapsed = Date.now() - timestamp;
// don't allow interaction on old requests
if (elapsed > (VERIFICATION_REQUEST_TIMEOUT - VERIFICATION_REQUEST_MARGIN) ||
elapsed < -(VERIFICATION_REQUEST_TIMEOUT / 2)
) {
this._observeOnly = true;
}
if (this.calculateEventTimeout(event) < VERIFICATION_REQUEST_MARGIN) {
this._observeOnly = true;
}
}
@@ -819,6 +840,8 @@ export class VerificationRequest extends EventEmitter {
this._eventsByThem.delete(type);
}
}
// also remember when we received the request event
this._requestReceivedAt = Date.now();
}
}
+9 -27
View File
@@ -84,12 +84,15 @@ export function PushProcessor(client) {
// $glob: RegExp,
};
const matchingRuleFromKindSet = (ev, kindset, device) => {
const matchingRuleFromKindSet = (ev, kindset) => {
for (let ruleKindIndex = 0;
ruleKindIndex < RULEKINDS_IN_ORDER.length;
++ruleKindIndex) {
const kind = RULEKINDS_IN_ORDER[ruleKindIndex];
const ruleset = kindset[kind];
if (!ruleset) {
continue;
}
for (let ruleIndex = 0; ruleIndex < ruleset.length; ++ruleIndex) {
const rule = ruleset[ruleIndex];
@@ -97,7 +100,7 @@ export function PushProcessor(client) {
continue;
}
const rawrule = templateRuleToRaw(kind, rule, device);
const rawrule = templateRuleToRaw(kind, rule);
if (!rawrule) {
continue;
}
@@ -111,7 +114,7 @@ export function PushProcessor(client) {
return null;
};
const templateRuleToRaw = function(kind, tprule, device) {
const templateRuleToRaw = function(kind, tprule) {
const rawrule = {
'rule_id': tprule.rule_id,
'actions': tprule.actions,
@@ -153,19 +156,12 @@ export function PushProcessor(client) {
});
break;
}
if (device) {
rawrule.conditions.push({
'kind': 'device',
'profile_tag': device,
});
}
return rawrule;
};
const eventFulfillsCondition = function(cond, ev) {
const condition_functions = {
"event_match": eventFulfillsEventMatchCondition,
"device": eventFulfillsDeviceCondition,
"contains_display_name": eventFulfillsDisplayNameCondition,
"room_member_count": eventFulfillsRoomMemberCountCondition,
"sender_notification_permission": eventFulfillsSenderNotifPermCondition,
@@ -257,10 +253,6 @@ export function PushProcessor(client) {
return content.body.search(pat) > -1;
};
const eventFulfillsDeviceCondition = function(cond, ev) {
return false; // XXX: Allow a profile tag to be set for the web client instance
};
const eventFulfillsEventMatchCondition = function(cond, ev) {
if (!cond.key) {
return false;
@@ -325,23 +317,13 @@ export function PushProcessor(client) {
};
const matchingRuleForEventWithRulesets = function(ev, rulesets) {
if (!rulesets || !rulesets.device) {
if (!rulesets) {
return null;
}
if (ev.getSender() == client.credentials.userId) {
if (ev.getSender() === client.credentials.userId) {
return null;
}
const allDevNames = Object.keys(rulesets.device);
for (let i = 0; i < allDevNames.length; ++i) {
const devname = allDevNames[i];
const devrules = rulesets.device[devname];
const matchingRule = matchingRuleFromKindSet(devrules, devname);
if (matchingRule) {
return matchingRule;
}
}
return matchingRuleFromKindSet(ev, rulesets.global);
};
@@ -392,7 +374,7 @@ export function PushProcessor(client) {
* @return {object} The push rule, or null if no such rule was found
*/
this.getPushRuleById = function(ruleId) {
for (const scope of ['device', 'global']) {
for (const scope of ['global']) {
if (client.pushRules[scope] === undefined) continue;
for (const kind of RULEKINDS_IN_ORDER) {
+815 -778
View File
File diff suppressed because it is too large Load Diff