Compare commits

...

20 Commits

Author SHA1 Message Date
RiotRobot 8b6b16067b v8.2.0-rc.1 2020-08-26 11:45:48 +01:00
RiotRobot a2da0de17d Prepare changelog for v8.2.0-rc.1 2020-08-26 11:45:48 +01:00
J. Ryan Stinnett 93ff3edb6b Merge pull request #1449 from matrix-org/jryans/strict-enc-check
Add state event check
2020-08-26 11:03:42 +01:00
J. Ryan Stinnett 7c67fd69dd Add state event check
State events are never encrypted, so we can ignore them here.
2020-08-26 10:55:03 +01:00
J. Ryan Stinnett ed978f69fb Merge pull request #1444 from matrix-org/jryans/secure-backup-required
Add method to check whether client .well-known has been fetched
2020-08-24 17:32:49 +01:00
J. Ryan Stinnett 743f2465ea Document WellKnown.client event 2020-08-24 15:56:14 +01:00
J. Ryan Stinnett 41fffa233a Switch to promise-based API 2020-08-24 15:56:14 +01:00
J. Ryan Stinnett e45377166b Merge pull request #1443 from matrix-org/jryans/cross-signing-auth-errors
Handle auth errors during cross-signing key upload
2020-08-24 11:52:40 +01:00
David Baker 24939bf0b0 Merge pull request #1448 from matrix-org/dbkr/dont_fail_if_audio_output_unavailable
Don't fail if the requested audio output isn't available
2020-08-21 16:19:20 +01:00
David Baker 3221be4855 Don't fail if the requested audio output isn't available
This thorws an exception if the requested device isn't available,
in which case we should catch it  & carry on with the default device.

Fixes https://github.com/vector-im/element-web/issues/15019
2020-08-21 16:02:08 +01:00
David Baker 3135f1ed24 Merge pull request #1447 from matrix-org/dbkr/fix_log_fail
Fix logging failures
2020-08-21 11:12:11 +01:00
David Baker 1b0834ffb0 Fix logging failures 2020-08-21 11:01:34 +01:00
David Baker d79d613cb7 Merge pull request #1446 from matrix-org/dbkr/log_user_media_constraints
Log the constraints we pass to getUserMedia
2020-08-19 18:30:39 +01:00
David Baker d8cc1f7b7a Log the constraints we pass to getUserMedia
To help debug voip calls
2020-08-19 18:13:44 +01:00
J. Ryan Stinnett d7c8856fdd Add method to check whether client .well-known has been fetched
This allows clearly detecting whether we have _ever_ fetched .well-known at all.
Without this, it's hard to be sure whether the value is `undefined` because the
fetch has not been attempted yet or because an error occurred.
2020-08-19 16:10:10 +01:00
J. Ryan Stinnett 9d80a332aa Upload cross-signing keys first to handle failure
This changes to uploading cross-signing keys first, since they require a valid
UI auth session, and so are more likely to fail than other API calls. With the
new ordering, if they do fail, then by failing first, we won't have made any
changes to the user's account, so everything rolls back correctly.
2020-08-19 11:55:09 +01:00
J. Ryan Stinnett e14f7b63c7 Handle auth errors during cross-signing key upload
In order to handle auth errors (such as incorrect passwords), we need to ensure
we only try to upload cross-signing keys from within the auth flow helper
function.

This rearranges things to store that function in the builder to use it when the
actual upload happens.
2020-08-19 11:44:41 +01:00
Travis Ralston 3bd2880923 Revert "Merge pull request #1440 from matrix-org/travis/spec-i18n"
This reverts commit 2401ad7159.
2020-08-18 13:07:31 -06:00
Travis Ralston 2401ad7159 Merge pull request #1440 from matrix-org/travis/spec-i18n
Use SAS emoji data from matrix-doc
2020-08-18 11:41:36 -06:00
Travis Ralston 5d95398621 Use SAS emoji data from matrix-doc
Fixes https://github.com/vector-im/element-web/issues/14947

Much like element-web's Jitsi wrapper build steps, this downloads the emoji JSON at build time to ensure it gets reasonably updated. In the future, the spec might want to consider publishing a dedicated i18n package on npm for this, however this is fine for now. We download rather than copy/paste to ensure we always have an updated copy.
2020-08-17 15:35:03 -06:00
8 changed files with 136 additions and 42 deletions
+19
View File
@@ -1,3 +1,22 @@
Changes in [8.2.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.2.0-rc.1) (2020-08-26)
==========================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.1.0...v8.2.0-rc.1)
* Add state event check
[\#1449](https://github.com/matrix-org/matrix-js-sdk/pull/1449)
* Add method to check whether client .well-known has been fetched
[\#1444](https://github.com/matrix-org/matrix-js-sdk/pull/1444)
* Handle auth errors during cross-signing key upload
[\#1443](https://github.com/matrix-org/matrix-js-sdk/pull/1443)
* Don't fail if the requested audio output isn't available
[\#1448](https://github.com/matrix-org/matrix-js-sdk/pull/1448)
* Fix logging failures
[\#1447](https://github.com/matrix-org/matrix-js-sdk/pull/1447)
* Log the constraints we pass to getUserMedia
[\#1446](https://github.com/matrix-org/matrix-js-sdk/pull/1446)
* Use SAS emoji data from matrix-doc
[\#1440](https://github.com/matrix-org/matrix-js-sdk/pull/1440)
Changes in [8.1.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v8.1.0) (2020-08-17)
================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v8.1.0-rc.1...v8.1.0)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "matrix-js-sdk",
"version": "8.1.0",
"version": "8.2.0-rc.1",
"description": "Matrix Client-Server SDK for Javascript",
"scripts": {
"prepare": "yarn build",
+53
View File
@@ -21,6 +21,7 @@ import * as olmlib from "../../../src/crypto/olmlib";
import {TestClient} from '../../TestClient';
import {HttpResponse, setHttpResponses} from '../../test-utils';
import {resetCrossSigningKeys, createSecretStorageKey} from "./crypto-utils";
import { MatrixError } from '../../../src/http-api';
async function makeTestClient(userInfo, options, keys) {
if (!keys) keys = {};
@@ -77,6 +78,58 @@ describe("Cross Signing", function() {
expect(alice.uploadDeviceSigningKeys).toHaveBeenCalled();
});
it("should abort bootstrap if device signing auth fails", async function() {
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
);
alice.uploadDeviceSigningKeys = async (auth, keys) => {
const errorResponse = {
session: "sessionId",
flows: [
{
stages: [
"m.login.password",
],
},
],
params: {},
};
// If we're not just polling for flows, add on error rejecting the
// auth attempt.
if (auth) {
Object.assign(errorResponse, {
completed: [],
error: "Invalid password",
errcode: "M_FORBIDDEN",
});
}
const error = new MatrixError(errorResponse);
error.httpStatus == 401;
throw error;
};
alice.uploadKeySignatures = async () => {};
alice.setAccountData = async () => {};
alice.getAccountDataFromServer = async () => { };
const authUploadDeviceSigningKeys = async func => await func({});
// Try bootstrap, expecting `authUploadDeviceSigningKeys` to pass
// through failure, stopping before actually applying changes.
let bootstrapDidThrow = false;
try {
await alice.bootstrapSecretStorage({
createSecretStorageKey,
authUploadDeviceSigningKeys,
});
} catch (e) {
if (e.errcode === "M_FORBIDDEN") {
bootstrapDidThrow = true;
}
}
expect(bootstrapDidThrow).toBeTruthy();
});
it("should upload a signature when a user is verified", async function() {
const alice = await makeTestClient(
{userId: "@alice:example.com", deviceId: "Osborne2"},
+21 -8
View File
@@ -357,6 +357,9 @@ export function MatrixClient(opts) {
this._cachedCapabilities = null; // { capabilities: {}, lastUpdated: timestamp }
this._clientWellKnown = undefined;
this._clientWellKnownPromise = undefined;
// The SDK doesn't really provide a clean way for events to recalculate the push
// actions for themselves, so we have to kinda help them out when they are encrypted.
// We do this so that push rules are correctly executed on events in their decrypted
@@ -4810,20 +4813,23 @@ MatrixClient.prototype.startClient = async function(opts) {
};
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);
}
// `getRawClientConfig` does not throw or reject on network errors, instead
// it absorbs errors and returns `{}`.
this._clientWellKnownPromise = AutoDiscovery.getRawClientConfig(
this.getDomain(),
);
this._clientWellKnown = await this._clientWellKnownPromise;
this.emit("WellKnown.client", this._clientWellKnown);
};
MatrixClient.prototype.getClientWellKnown = function() {
return this._clientWellKnown;
};
MatrixClient.prototype.waitForClientWellKnown = function() {
return this._clientWellKnownPromise;
};
/**
* store client options with boolean/string/numeric values
* to know in the next session what flags the sync data was
@@ -5698,6 +5704,13 @@ MatrixClient.prototype.generateClientSecret = function() {
* @param {string} data.request_id The ID of the original request.
*/
/**
* Fires when the client .well-known info is fetched.
*
* @event module:client~MatrixClient#"WellKnown.client"
* @param {object} data The JSON object returned by the server
*/
// EventEmitter JSDocs
/**
+23 -14
View File
@@ -18,7 +18,7 @@ import {
*/
export class EncryptionSetupBuilder {
/**
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
* @param {Object.<String, MatrixEvent>} accountData pre-existing account data, will only be read, not written.
* @param {CryptoCallbacks} delegateCryptoCallbacks crypto callbacks to delegate to if the key isn't in cache yet
*/
constructor(accountData, delegateCryptoCallbacks) {
@@ -33,11 +33,17 @@ export class EncryptionSetupBuilder {
/**
* Adds new cross-signing public keys
* @param {Object} auth auth dictionary needed to upload the new keys
*
* @param {function} authUpload Function called to await an interactive auth
* flow when uploading device signing keys.
* Args:
* {function} A function that makes the request requiring auth. Receives
* the auth data as an object. Can be called multiple times, first with
* an empty authDict, to obtain the flows.
* @param {Object} keys the new keys
*/
addCrossSigningKeys(auth, keys) {
this._crossSigningKeys = {auth, keys};
addCrossSigningKeys(authUpload, keys) {
this._crossSigningKeys = {authUpload, keys};
}
/**
@@ -165,25 +171,28 @@ export class EncryptionSetupOperation {
*/
async apply(crypto) {
const baseApis = crypto._baseApis;
// set account data
if (this._accountData) {
for (const [type, content] of this._accountData) {
await baseApis.setAccountData(type, content);
}
}
// upload cross-signing keys
if (this._crossSigningKeys) {
const keys = {};
for (const [name, key] of Object.entries(this._crossSigningKeys.keys)) {
keys[name + "_key"] = key;
}
await baseApis.uploadDeviceSigningKeys(
this._crossSigningKeys.auth,
keys,
);
// We must only call `uploadDeviceSigningKeys` from inside this auth
// helper to ensure we properly handle auth errors.
await this._crossSigningKeys.authUpload(authDict => {
return baseApis.uploadDeviceSigningKeys(authDict, keys);
});
// pass the new keys to the main instance of our own CrossSigningInfo.
crypto._crossSigningInfo.setKeys(this._crossSigningKeys.keys);
}
// set account data
if (this._accountData) {
for (const [type, content] of this._accountData) {
await baseApis.setAccountData(type, content);
}
}
// upload first cross-signing signatures with the new key
// (e.g. signing our own device)
if (this._keySignatures) {
+5 -12
View File
@@ -453,7 +453,8 @@ Crypto.prototype.isCrossSigningReady = async function() {
* called to await an interactive auth flow when uploading device signing keys.
* Args:
* {function} A function that makes the request requiring auth. Receives the
* auth data as an object. Can be called multiple times, first with an empty authDict, to obtain the flows.
* auth data as an object. Can be called multiple times, first with an empty
* authDict, to obtain the flows.
* @param {function} [opts.createSecretStorageKey] Optional. Function
* called to await a secret storage key creation flow.
* Returns:
@@ -525,17 +526,9 @@ Crypto.prototype.bootstrapSecretStorage = async function({
// sign master key with device key
await this._signObject(crossSigningInfo.keys.master);
await authUploadDeviceSigningKeys(authDict => {
if (authDict) {
builder.addCrossSigningKeys(authDict, crossSigningInfo.keys);
return Promise.resolve();
} else {
// This callback also gets called to obtain the IUA flows,
// so do a call to obtain those if we don't have the authDict yet
// We should get called again at a later point with the authDict.
return this._baseApis.uploadDeviceSigningKeys(null, {});
}
});
// Store auth flow helper function, as we need to call it when uploading
// to ensure we handle auth errors properly.
builder.addCrossSigningKeys(authUploadDeviceSigningKeys, crossSigningInfo.keys);
// cross-sign own device
const device = this._deviceList.getStoredDevice(this._userId, this._deviceId);
+1 -1
View File
@@ -626,7 +626,7 @@ utils.extend(MatrixEvent.prototype, {
* @return {boolean} True if this event is encrypted.
*/
isEncrypted: function() {
return this.event.type === "m.room.encrypted";
return !this.isState() && this.event.type === "m.room.encrypted";
},
/**
+13 -6
View File
@@ -403,7 +403,7 @@ MatrixCall.prototype._initWithHangup = function(event) {
* Answer a call.
*/
MatrixCall.prototype.answer = function() {
debuglog("Answering call %s of type %s", this.callId, this.type);
debuglog(`Answering call ${this.callId} of type ${this.type}`);
const self = this;
if (self._answerContent) {
@@ -412,8 +412,10 @@ MatrixCall.prototype.answer = function() {
}
if (!this.localAVStream && !this.waitForLocalAVStream) {
const constraints = _getUserMediaVideoContraints(this.type);
logger.log("Getting user media with constraints", constraints);
this.webRtc.getUserMedia(
_getUserMediaVideoContraints(this.type),
constraints,
hookCallback(self, self._maybeGotUserMediaForAnswer),
hookCallback(self, self._maybeGotUserMediaForAnswer),
);
@@ -1065,7 +1067,7 @@ const terminate = function(self, hangupParty, hangupReason, shouldEmit) {
};
const stopAllMedia = function(self) {
debuglog("stopAllMedia (stream=%s)", self.localAVStream);
debuglog(`stopAllMedia (stream=${self.localAVStream})`);
if (self.localAVStream) {
forAllTracksOnStream(self.localAVStream, function(t) {
if (t.stop) {
@@ -1127,7 +1129,11 @@ const _tryPlayRemoteAudioStream = async function(self) {
const player = self.getRemoteAudioElement();
// if audioOutput is non-default:
if (audioOutput) await player.setSinkId(audioOutput);
try {
if (audioOutput) await player.setSinkId(audioOutput);
} catch (e) {
logger.warn("Couldn't set requested audio output device: using default", e);
}
player.autoplay = true;
self.assignElement(player, self.remoteAStream, "remoteAudio");
@@ -1188,8 +1194,8 @@ const _sendCandidateQueue = function(self) {
if (self.candidateSendTries > 5) {
debuglog(
"Failed to send candidates on attempt %s. Giving up for now.",
self.candidateSendTries,
"Failed to send candidates on attempt " + self.candidateSendTries +
". Giving up for now.",
);
self.candidateSendTries = 0;
return;
@@ -1205,6 +1211,7 @@ const _sendCandidateQueue = function(self) {
};
const _placeCallWithConstraints = function(self, constraints) {
logger.log("Getting user media with constraints", constraints);
self.client.callList[self.callId] = self;
self.webRtc.getUserMedia(
constraints,