Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Support Matrix 1.1 (drop legacy r0 versions) (#9819)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Telatynski <[email protected]>
  • Loading branch information
turt2live and t3chguy authored Aug 14, 2023
1 parent f9e79fd commit 180fcaa
Show file tree
Hide file tree
Showing 32 changed files with 709 additions and 437 deletions.
6 changes: 3 additions & 3 deletions cypress/e2e/read-receipts/read-receipts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ describe("Read receipts", () => {
cy.intercept({
method: "POST",
url: new RegExp(
`http://localhost:\\d+/_matrix/client/r0/rooms/${uriEncodedOtherRoomId}/receipt/m\\.read/.+`,
`http://localhost:\\d+/_matrix/client/v3/rooms/${uriEncodedOtherRoomId}/receipt/m\\.read/.+`,
),
}).as("receiptRequest");

Expand Down Expand Up @@ -321,7 +321,7 @@ describe("Read receipts", () => {

cy.intercept({
method: "POST",
url: new RegExp(`http://localhost:\\d+/_matrix/client/r0/rooms/${uriEncodedOtherRoomId}/read_markers`),
url: new RegExp(`http://localhost:\\d+/_matrix/client/v3/rooms/${uriEncodedOtherRoomId}/read_markers`),
}).as("readMarkersRequest");

cy.findByRole("button", { name: "Jump to first unread message." }).click();
Expand All @@ -341,7 +341,7 @@ describe("Read receipts", () => {

cy.intercept({
method: "POST",
url: new RegExp(`http://localhost:\\d+/_matrix/client/r0/rooms/${uriEncodedOtherRoomId}/read_markers`),
url: new RegExp(`http://localhost:\\d+/_matrix/client/v3/rooms/${uriEncodedOtherRoomId}/read_markers`),
}).as("readMarkersRequest");

cy.findByRole("button", { name: "Scroll to most recent messages" }).click();
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/timeline/timeline.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,14 +704,14 @@ describe("Timeline", () => {
});

it("should render url previews", () => {
cy.intercept("**/_matrix/media/r0/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*", {
cy.intercept("**/_matrix/media/v3/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*", {
statusCode: 200,
fixture: "riot.png",
headers: {
"Content-Type": "image/png",
},
}).as("mxc");
cy.intercept("**/_matrix/media/r0/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*", {
cy.intercept("**/_matrix/media/v3/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*", {
statusCode: 200,
body: {
"og:title": "Element Call",
Expand Down
2 changes: 1 addition & 1 deletion cypress/support/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ declare global {
Cypress.Commands.add(
"loginUser",
(homeserver: HomeserverInstance, username: string, password: string): Chainable<UserCredentials> => {
const url = `${homeserver.baseUrl}/_matrix/client/r0/login`;
const url = `${homeserver.baseUrl}/_matrix/client/v3/login`;
return cy
.request<{
access_token: string;
Expand Down
297 changes: 131 additions & 166 deletions src/AddThreepid.ts

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions src/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { decryptAES, encryptAES, IEncryptedPayload } from "matrix-js-sdk/src/cry
import { QueryDict } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { SSOAction } from "matrix-js-sdk/src/@types/auth";
import { MINIMUM_MATRIX_VERSION } from "matrix-js-sdk/src/version-support";

Check failure on line 27 in src/Lifecycle.ts

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Cannot find module 'matrix-js-sdk/src/version-support' or its corresponding type declarations.

Check failure on line 27 in src/Lifecycle.ts

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Cannot find module 'matrix-js-sdk/src/version-support' or its corresponding type declarations.

import { IMatrixClientCreds, MatrixClientPeg } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security";
Expand Down Expand Up @@ -66,6 +67,7 @@ import { SdkContextClass } from "./contexts/SDKContext";
import { messageForLoginError } from "./utils/ErrorUtils";
import { completeOidcLogin } from "./utils/oidc/authorize";
import { persistOidcAuthenticatedSettings } from "./utils/oidc/persistOidcSettings";
import GenericToast from "./components/views/toasts/GenericToast";

const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url";
Expand Down Expand Up @@ -584,13 +586,43 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
},
false,
);
checkServerVersions();
return true;
} else {
logger.log("No previous session found.");
return false;
}
}

async function checkServerVersions(): Promise<void> {
MatrixClientPeg.get()
?.getVersions()
.then((response) => {
if (!response.versions.includes(MINIMUM_MATRIX_VERSION)) {
const toastKey = "LEGACY_SERVER";
ToastStore.sharedInstance().addOrReplaceToast({
key: toastKey,
title: _t("Your server is unsupported"),
props: {
description: _t(
"This server is using an older version of Matrix. Upgrade to Matrix %(version)s to use %(brand)s without errors.",
{
version: MINIMUM_MATRIX_VERSION,
brand: SdkConfig.get().brand,
},
),
acceptLabel: _t("OK"),
onAccept: () => {
ToastStore.sharedInstance().dismissToast(toastKey);
},
},
component: GenericToast,
priority: 98,
});
}
});
}

async function handleLoadSessionFailure(e: unknown): Promise<boolean> {
logger.error("Unable to load session", e);

Expand Down
55 changes: 12 additions & 43 deletions src/components/structures/auth/ForgotPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ limitations under the License.

import React, { ReactNode } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { createClient } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";

import { _t, _td } from "../../../languageHandler";
Expand Down Expand Up @@ -81,7 +80,6 @@ interface State {
serverIsAlive: boolean;
serverDeadError: string;

serverSupportsControlOfDevicesLogout: boolean;
logoutDevices: boolean;
}

Expand All @@ -104,26 +102,18 @@ export default class ForgotPassword extends React.Component<Props, State> {
// be seeing.
serverIsAlive: true,
serverDeadError: "",
serverSupportsControlOfDevicesLogout: false,
logoutDevices: false,
};
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
}

public componentDidMount(): void {
this.checkServerCapabilities(this.props.serverConfig);
}

public componentDidUpdate(prevProps: Readonly<Props>): void {
if (
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
) {
// Do a liveliness check on the new URLs
this.checkServerLiveliness(this.props.serverConfig);

// Do capabilities check on new URLs
this.checkServerCapabilities(this.props.serverConfig);
}
}

Expand All @@ -146,19 +136,6 @@ export default class ForgotPassword extends React.Component<Props, State> {
}
}

private async checkServerCapabilities(serverConfig: ValidatedServerConfig): Promise<void> {
const tempClient = createClient({
baseUrl: serverConfig.hsUrl,
});

const serverSupportsControlOfDevicesLogout = await tempClient.doesServerSupportLogoutDevices();

this.setState({
logoutDevices: !serverSupportsControlOfDevicesLogout,
serverSupportsControlOfDevicesLogout,
});
}

private async onPhaseEmailInputSubmit(): Promise<void> {
this.phase = Phase.SendingEmail;

Expand Down Expand Up @@ -376,16 +353,10 @@ export default class ForgotPassword extends React.Component<Props, State> {
description: (
<div>
<p>
{!this.state.serverSupportsControlOfDevicesLogout
? _t(
"Resetting your password on this homeserver will cause all of your devices to be " +
"signed out. This will delete the message encryption keys stored on them, " +
"making encrypted chat history unreadable.",
)
: _t(
"Signing out your devices will delete the message encryption keys stored on them, " +
"making encrypted chat history unreadable.",
)}
{_t(
"Signing out your devices will delete the message encryption keys stored on them, " +
"making encrypted chat history unreadable.",
)}
</p>
<p>
{_t(
Expand Down Expand Up @@ -446,16 +417,14 @@ export default class ForgotPassword extends React.Component<Props, State> {
autoComplete="new-password"
/>
</div>
{this.state.serverSupportsControlOfDevicesLogout ? (
<div className="mx_AuthBody_fieldRow">
<StyledCheckbox
onChange={() => this.setState({ logoutDevices: !this.state.logoutDevices })}
checked={this.state.logoutDevices}
>
{_t("Sign out of all devices")}
</StyledCheckbox>
</div>
) : null}
<div className="mx_AuthBody_fieldRow">
<StyledCheckbox
onChange={() => this.setState({ logoutDevices: !this.state.logoutDevices })}
checked={this.state.logoutDevices}
>
{_t("Sign out of all devices")}
</StyledCheckbox>
</div>
{this.state.errorText && <ErrorMessage message={this.state.errorText} />}
<button type="submit" className="mx_Login_submit">
{submitButtonChild}
Expand Down
81 changes: 5 additions & 76 deletions src/components/views/settings/ChangePassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ limitations under the License.
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";

import type ExportE2eKeysDialog from "../../../async-components/views/dialogs/security/ExportE2eKeysDialog";
import Field from "../elements/Field";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import AccessibleButton from "../elements/AccessibleButton";
Expand All @@ -29,7 +28,6 @@ import Modal from "../../../Modal";
import PassphraseField from "../auth/PassphraseField";
import { PASSWORD_MIN_SCORE } from "../auth/RegistrationForm";
import SetEmailDialog from "../dialogs/SetEmailDialog";
import QuestionDialog from "../dialogs/QuestionDialog";

const FIELD_OLD_PASSWORD = "field_old_password";
const FIELD_NEW_PASSWORD = "field_new_password";
Expand All @@ -43,11 +41,7 @@ enum Phase {
}

interface IProps {
onFinished: (outcome: {
didSetEmail?: boolean;
/** Was one or more other devices logged out whilst changing the password */
didLogoutOutOtherDevices: boolean;
}) => void;
onFinished: (outcome: { didSetEmail?: boolean }) => void;
onError: (error: Error) => void;
rowClassName?: string;
buttonClassName?: string;
Expand Down Expand Up @@ -95,58 +89,10 @@ export default class ChangePassword extends React.Component<IProps, IState> {
private async onChangePassword(oldPassword: string, newPassword: string): Promise<void> {
const cli = MatrixClientPeg.safeGet();

// if the server supports it then don't sign user out of all devices
const serverSupportsControlOfDevicesLogout = await cli.doesServerSupportLogoutDevices();
const userHasOtherDevices = (await cli.getDevices()).devices.length > 1;

if (userHasOtherDevices && !serverSupportsControlOfDevicesLogout && this.props.confirm) {
// warn about logging out all devices
const { finished } = Modal.createDialog(QuestionDialog, {
title: _t("Warning!"),
description: (
<div>
<p>
{_t(
"Changing your password on this homeserver will cause all of your other devices to be " +
"signed out. This will delete the message encryption keys stored on them, and may make " +
"encrypted chat history unreadable.",
)}
</p>
<p>
{_t(
"If you want to retain access to your chat history in encrypted rooms you should first " +
"export your room keys and re-import them afterwards.",
)}
</p>
<p>
{_t(
"You can also ask your homeserver admin to upgrade the server to change this behaviour.",
)}
</p>
</div>
),
button: _t("Continue"),
extraButtons: [
<button key="exportRoomKeys" className="mx_Dialog_primary" onClick={this.onExportE2eKeysClicked}>
{_t("Export E2E room keys")}
</button>,
],
});

const [confirmed] = await finished;
if (!confirmed) return;
}

this.changePassword(cli, oldPassword, newPassword, serverSupportsControlOfDevicesLogout, userHasOtherDevices);
this.changePassword(cli, oldPassword, newPassword);
}

private changePassword(
cli: MatrixClient,
oldPassword: string,
newPassword: string,
serverSupportsControlOfDevicesLogout: boolean,
userHasOtherDevices: boolean,
): void {
private changePassword(cli: MatrixClient, oldPassword: string, newPassword: string): void {
const authDict = {
type: "m.login.password",
identifier: {
Expand All @@ -163,23 +109,17 @@ export default class ChangePassword extends React.Component<IProps, IState> {
phase: Phase.Uploading,
});

const logoutDevices = serverSupportsControlOfDevicesLogout ? false : undefined;

// undefined or true mean all devices signed out
const didLogoutOutOtherDevices = !serverSupportsControlOfDevicesLogout && userHasOtherDevices;

cli.setPassword(authDict, newPassword, logoutDevices)
cli.setPassword(authDict, newPassword, false)
.then(
() => {
if (this.props.shouldAskForEmail) {
return this.optionallySetEmail().then((confirmed) => {
this.props.onFinished({
didSetEmail: confirmed,
didLogoutOutOtherDevices,
});
});
} else {
this.props.onFinished({ didLogoutOutOtherDevices });
this.props.onFinished({});
}
},
(err) => {
Expand Down Expand Up @@ -229,17 +169,6 @@ export default class ChangePassword extends React.Component<IProps, IState> {
return modal.finished.then(([confirmed]) => !!confirmed);
}

private onExportE2eKeysClicked = (): void => {
Modal.createDialogAsync(
import("../../../async-components/views/dialogs/security/ExportE2eKeysDialog") as unknown as Promise<
typeof ExportE2eKeysDialog
>,
{
matrixClient: MatrixClientPeg.safeGet(),
},
);
};

private markFieldValid(fieldID: FieldType, valid?: boolean): void {
const { fieldValid } = this.state;
fieldValid[fieldID] = valid;
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/settings/account/PhoneNumbers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
?.haveMsisdnToken(token)
.then(([finished] = []) => {
let newPhoneNumber = this.state.newPhoneNumber;
if (finished) {
if (finished !== false) {
const msisdns = [...this.props.msisdns, { address, medium: ThreepidMedium.Phone }];
this.props.onMsisdnsChange(msisdns);
newPhoneNumber = "";
Expand Down
Loading

0 comments on commit 180fcaa

Please sign in to comment.