2016-08-03 11:34:13 +01:00
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2024 New Vector Ltd.
|
2020-04-06 15:42:06 -06:00
|
|
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2016 OpenMarket Ltd
|
2016-08-03 11:34:13 +01:00
|
|
|
|
2024-09-09 14:57:16 +01:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2016-08-03 11:34:13 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from "react";
|
2021-07-07 11:08:53 +01:00
|
|
|
import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
|
2021-10-22 17:23:32 -05:00
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2023-07-04 14:49:27 +01:00
|
|
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
2016-08-03 11:34:13 +01:00
|
|
|
|
2021-06-29 13:11:58 +01:00
|
|
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
2017-05-30 16:09:57 +02:00
|
|
|
import { _t } from "../../../languageHandler";
|
2022-05-20 19:14:17 +02:00
|
|
|
import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback } from "../../structures/InteractiveAuth";
|
2024-06-21 15:16:22 +01:00
|
|
|
import { ContinueKind, SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
|
2020-05-28 22:33:00 +01:00
|
|
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
2021-07-02 17:08:27 +02:00
|
|
|
import BaseDialog from "./BaseDialog";
|
2022-03-24 16:11:01 -06:00
|
|
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
|
|
|
|
import { Action } from "../../../dispatcher/actions";
|
2020-04-06 15:42:06 -06:00
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
type DialogAesthetics = Partial<{
|
|
|
|
|
[x in AuthType]: {
|
|
|
|
|
[x: number]: {
|
|
|
|
|
body: string;
|
|
|
|
|
continueText?: string;
|
2023-12-20 10:58:24 +00:00
|
|
|
continueKind?: ContinueKind;
|
2023-02-13 11:39:16 +00:00
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}>;
|
|
|
|
|
|
2021-06-14 21:53:44 +01:00
|
|
|
interface IProps {
|
2023-02-28 10:31:48 +00:00
|
|
|
onFinished: (success?: boolean) => void;
|
2021-06-14 21:53:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface IState {
|
|
|
|
|
shouldErase: boolean;
|
2023-03-07 10:45:55 +00:00
|
|
|
errStr: string | null;
|
2021-06-14 21:53:44 +01:00
|
|
|
authData: any; // for UIA
|
|
|
|
|
authEnabled: boolean; // see usages for information
|
|
|
|
|
|
|
|
|
|
// A few strings that are passed to InteractiveAuth for design or are displayed
|
|
|
|
|
// next to the InteractiveAuth component.
|
2023-03-07 10:45:55 +00:00
|
|
|
bodyText?: string;
|
|
|
|
|
continueText?: string;
|
2023-12-20 10:58:24 +00:00
|
|
|
continueKind?: ContinueKind;
|
2021-06-14 21:53:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default class DeactivateAccountDialog extends React.Component<IProps, IState> {
|
2023-02-13 11:39:16 +00:00
|
|
|
public constructor(props: IProps) {
|
2019-12-17 17:26:12 +00:00
|
|
|
super(props);
|
2016-08-03 11:34:13 +01:00
|
|
|
|
|
|
|
|
this.state = {
|
2018-10-29 22:57:33 +00:00
|
|
|
shouldErase: false,
|
2016-08-03 11:34:13 +01:00
|
|
|
errStr: null,
|
2020-04-06 15:42:06 -06:00
|
|
|
authData: null, // for UIA
|
2020-05-12 17:17:17 -06:00
|
|
|
authEnabled: true, // see usages for information
|
2016-08-03 11:34:13 +01:00
|
|
|
};
|
2024-11-01 17:39:08 +00:00
|
|
|
}
|
2016-08-03 11:34:13 +01:00
|
|
|
|
2024-11-01 17:39:08 +00:00
|
|
|
public componentDidMount(): void {
|
2021-06-14 21:53:44 +01:00
|
|
|
this.initAuth(/* shouldErase= */ false);
|
2020-04-06 15:42:06 -06:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 11:39:16 +00:00
|
|
|
private onStagePhaseChange = (stage: AuthType, phase: number): void => {
|
2020-04-22 21:37:52 +01:00
|
|
|
const dialogAesthetics = {
|
|
|
|
|
[SSOAuthEntry.PHASE_PREAUTH]: {
|
2023-10-03 19:17:26 +01:00
|
|
|
body: _t("settings|general|deactivate_confirm_body_sso"),
|
2023-09-12 08:36:06 +01:00
|
|
|
continueText: _t("auth|sso"),
|
2020-04-22 21:37:52 +01:00
|
|
|
continueKind: "danger",
|
|
|
|
|
},
|
|
|
|
|
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
2023-10-03 19:17:26 +01:00
|
|
|
body: _t("settings|general|deactivate_confirm_body"),
|
|
|
|
|
continueText: _t("settings|general|deactivate_confirm_continue"),
|
2020-04-22 21:37:52 +01:00
|
|
|
continueKind: "danger",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// This is the same as aestheticsForStagePhases in InteractiveAuthDialog minus the `title`
|
2023-02-13 11:39:16 +00:00
|
|
|
const DEACTIVATE_AESTHETICS: DialogAesthetics = {
|
2020-04-22 21:37:52 +01:00
|
|
|
[SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics,
|
|
|
|
|
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-06 15:42:06 -06:00
|
|
|
const aesthetics = DEACTIVATE_AESTHETICS[stage];
|
2023-03-07 10:45:55 +00:00
|
|
|
let bodyText: string | undefined;
|
|
|
|
|
let continueText: string | undefined;
|
2023-12-20 10:58:24 +00:00
|
|
|
let continueKind: ContinueKind | undefined;
|
2020-04-06 15:42:06 -06:00
|
|
|
if (aesthetics) {
|
|
|
|
|
const phaseAesthetics = aesthetics[phase];
|
2023-03-07 10:45:55 +00:00
|
|
|
if (phaseAesthetics) {
|
|
|
|
|
if (phaseAesthetics.body) bodyText = phaseAesthetics.body;
|
|
|
|
|
if (phaseAesthetics.continueText) continueText = phaseAesthetics.continueText;
|
|
|
|
|
if (phaseAesthetics.continueKind) continueKind = phaseAesthetics.continueKind;
|
|
|
|
|
}
|
2020-04-06 15:42:06 -06:00
|
|
|
}
|
2021-06-29 13:11:58 +01:00
|
|
|
this.setState({ bodyText, continueText, continueKind });
|
2020-04-06 15:42:06 -06:00
|
|
|
};
|
|
|
|
|
|
2023-08-22 23:15:35 +12:00
|
|
|
private onUIAuthFinished: InteractiveAuthCallback<Awaited<ReturnType<MatrixClient["deactivateAccount"]>>> = async (
|
2023-07-04 14:49:27 +01:00
|
|
|
success,
|
|
|
|
|
result,
|
|
|
|
|
) => {
|
2020-04-06 15:42:06 -06:00
|
|
|
if (success) return; // great! makeRequest() will be called too.
|
|
|
|
|
|
|
|
|
|
if (result === ERROR_USER_CANCELLED) {
|
2021-06-14 21:53:44 +01:00
|
|
|
this.onCancel();
|
2018-05-23 13:35:42 +01:00
|
|
|
return;
|
|
|
|
|
}
|
2018-05-23 16:33:32 +01:00
|
|
|
|
2021-10-15 16:30:53 +02:00
|
|
|
logger.error("Error during UI Auth:", { result });
|
2023-10-03 19:17:26 +01:00
|
|
|
this.setState({ errStr: _t("settings|general|error_deactivate_communication") });
|
2020-04-06 15:42:06 -06:00
|
|
|
};
|
|
|
|
|
|
2022-11-03 12:50:07 +00:00
|
|
|
private onUIAuthComplete = (auth: IAuthData | null): void => {
|
2021-07-07 11:08:53 +01:00
|
|
|
// XXX: this should be returning a promise to maintain the state inside the state machine correct
|
|
|
|
|
// but given that a deactivation is followed by a local logout and all object instances being thrown away
|
|
|
|
|
// this isn't done.
|
2023-06-15 08:46:19 +01:00
|
|
|
MatrixClientPeg.safeGet()
|
2023-07-12 15:56:51 +01:00
|
|
|
.deactivateAccount(auth ?? undefined, this.state.shouldErase)
|
2020-04-06 15:42:06 -06:00
|
|
|
.then((r) => {
|
|
|
|
|
// Deactivation worked - logout & close this dialog
|
2022-03-24 16:11:01 -06:00
|
|
|
defaultDispatcher.fire(Action.TriggerLogout);
|
2020-04-06 15:42:06 -06:00
|
|
|
this.props.onFinished(true);
|
|
|
|
|
})
|
|
|
|
|
.catch((e) => {
|
2021-10-15 16:30:53 +02:00
|
|
|
logger.error(e);
|
2023-10-03 19:17:26 +01:00
|
|
|
this.setState({ errStr: _t("settings|general|error_deactivate_communication") });
|
2020-04-06 15:42:06 -06:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2021-06-14 21:53:44 +01:00
|
|
|
private onEraseFieldChange = (ev: React.FormEvent<HTMLInputElement>): void => {
|
2020-04-06 15:42:06 -06:00
|
|
|
this.setState({
|
2021-06-14 21:53:44 +01:00
|
|
|
shouldErase: ev.currentTarget.checked,
|
2020-05-12 17:17:17 -06:00
|
|
|
|
|
|
|
|
// Disable the auth form because we're going to have to reinitialize the auth
|
|
|
|
|
// information. We do this because we can't modify the parameters in the UIA
|
|
|
|
|
// session, and the user will have selected something which changes the request.
|
|
|
|
|
// Therefore, we throw away the last auth session and try a new one.
|
|
|
|
|
authEnabled: false,
|
2020-04-06 15:42:06 -06:00
|
|
|
});
|
2020-05-12 17:17:17 -06:00
|
|
|
|
|
|
|
|
// As mentioned above, set up for auth again to get updated UIA session info
|
2021-06-14 21:53:44 +01:00
|
|
|
this.initAuth(/* shouldErase= */ ev.currentTarget.checked);
|
2020-04-06 15:42:06 -06:00
|
|
|
};
|
2016-08-03 11:34:13 +01:00
|
|
|
|
2021-06-14 21:53:44 +01:00
|
|
|
private onCancel(): void {
|
2016-08-03 11:34:13 +01:00
|
|
|
this.props.onFinished(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-14 21:53:44 +01:00
|
|
|
private initAuth(shouldErase: boolean): void {
|
2023-06-15 08:46:19 +01:00
|
|
|
MatrixClientPeg.safeGet()
|
2023-07-12 15:56:51 +01:00
|
|
|
.deactivateAccount(undefined, shouldErase)
|
2020-05-12 17:17:17 -06:00
|
|
|
.then((r) => {
|
|
|
|
|
// If we got here, oops. The server didn't require any auth.
|
|
|
|
|
// Our application lifecycle will catch the error and do the logout bits.
|
|
|
|
|
// We'll try to log something in an vain attempt to record what happened (storage
|
|
|
|
|
// is also obliterated on logout).
|
2021-10-15 16:31:29 +02:00
|
|
|
logger.warn("User's account got deactivated without confirmation: Server had no auth");
|
2023-10-03 19:17:26 +01:00
|
|
|
this.setState({ errStr: _t("settings|general|error_deactivate_no_auth") });
|
2020-05-12 17:17:17 -06:00
|
|
|
})
|
|
|
|
|
.catch((e) => {
|
|
|
|
|
if (e && e.httpStatus === 401 && e.data) {
|
|
|
|
|
// Valid UIA response
|
2021-06-29 13:11:58 +01:00
|
|
|
this.setState({ authData: e.data, authEnabled: true });
|
2020-05-12 17:17:17 -06:00
|
|
|
} else {
|
2023-10-03 19:17:26 +01:00
|
|
|
this.setState({ errStr: _t("settings|general|error_deactivate_invalid_auth") });
|
2020-05-12 17:17:17 -06:00
|
|
|
}
|
|
|
|
|
});
|
2020-05-12 17:20:26 -06:00
|
|
|
}
|
2020-05-12 17:17:17 -06:00
|
|
|
|
2023-02-13 17:01:43 +00:00
|
|
|
public render(): React.ReactNode {
|
2023-03-07 10:45:55 +00:00
|
|
|
let error: JSX.Element | undefined;
|
2016-08-03 11:34:13 +01:00
|
|
|
if (this.state.errStr) {
|
|
|
|
|
error = <div className="error">{this.state.errStr}</div>;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-22 18:47:33 +01:00
|
|
|
let auth = <div>{_t("common|loading")}</div>;
|
2020-05-12 17:17:17 -06:00
|
|
|
if (this.state.authData && this.state.authEnabled) {
|
2020-04-06 15:42:06 -06:00
|
|
|
auth = (
|
|
|
|
|
<div>
|
2021-07-19 22:43:11 +01:00
|
|
|
{this.state.bodyText}
|
2020-04-06 15:42:06 -06:00
|
|
|
<InteractiveAuth
|
2023-06-15 08:46:19 +01:00
|
|
|
matrixClient={MatrixClientPeg.safeGet()}
|
2020-04-06 15:42:06 -06:00
|
|
|
authData={this.state.authData}
|
2021-08-11 21:50:26 +01:00
|
|
|
// XXX: onUIAuthComplete breaches the expected method contract, it gets away with it because it
|
|
|
|
|
// knows the entire app is about to die as a result of the account deactivation.
|
2021-07-07 11:08:53 +01:00
|
|
|
makeRequest={this.onUIAuthComplete as any}
|
2021-06-14 21:53:44 +01:00
|
|
|
onAuthFinished={this.onUIAuthFinished}
|
|
|
|
|
onStagePhaseChange={this.onStagePhaseChange}
|
2020-04-06 15:42:06 -06:00
|
|
|
continueText={this.state.continueText}
|
|
|
|
|
continueKind={this.state.continueKind}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2016-08-03 11:34:13 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-29 13:38:50 +00:00
|
|
|
// this is on purpose not a <form /> to prevent Enter triggering submission, to further prevent accidents
|
2016-08-03 11:34:13 +01:00
|
|
|
return (
|
2022-02-09 14:25:58 +00:00
|
|
|
<BaseDialog
|
|
|
|
|
className="mx_DeactivateAccountDialog"
|
2017-12-18 23:08:51 +13:00
|
|
|
onFinished={this.props.onFinished}
|
|
|
|
|
titleClass="danger"
|
2023-09-26 18:35:55 +01:00
|
|
|
title={_t("settings|general|deactivate_section")}
|
2022-02-09 14:25:58 +00:00
|
|
|
screenName="DeactivateAccount"
|
2018-05-23 16:33:32 +01:00
|
|
|
>
|
2016-08-03 11:34:13 +01:00
|
|
|
<div className="mx_Dialog_content">
|
2023-10-03 19:17:26 +01:00
|
|
|
<p>{_t("settings|general|deactivate_confirm_content")}</p>
|
2022-04-21 11:37:38 +02:00
|
|
|
<ul>
|
2023-10-03 19:17:26 +01:00
|
|
|
<li>{_t("settings|general|deactivate_confirm_content_1")}</li>
|
|
|
|
|
<li>{_t("settings|general|deactivate_confirm_content_2")}</li>
|
|
|
|
|
<li>{_t("settings|general|deactivate_confirm_content_3")}</li>
|
|
|
|
|
<li>{_t("settings|general|deactivate_confirm_content_4")}</li>
|
|
|
|
|
<li>{_t("settings|general|deactivate_confirm_content_5")}</li>
|
2022-04-21 11:37:38 +02:00
|
|
|
</ul>
|
2023-10-03 19:17:26 +01:00
|
|
|
<p>{_t("settings|general|deactivate_confirm_content_6")}</p>
|
2016-08-04 10:53:07 +01:00
|
|
|
|
2018-05-24 11:50:45 +01:00
|
|
|
<div className="mx_DeactivateAccountDialog_input_section">
|
|
|
|
|
<p>
|
2021-06-14 21:53:44 +01:00
|
|
|
<StyledCheckbox checked={this.state.shouldErase} onChange={this.onEraseFieldChange}>
|
2023-10-03 19:17:26 +01:00
|
|
|
{_t("settings|general|deactivate_confirm_erase_label")}
|
2020-05-28 22:33:00 +01:00
|
|
|
</StyledCheckbox>
|
2018-05-24 11:50:45 +01:00
|
|
|
</p>
|
2021-07-19 22:43:11 +01:00
|
|
|
{error}
|
|
|
|
|
{auth}
|
2018-05-24 11:50:45 +01:00
|
|
|
</div>
|
2016-08-03 11:34:13 +01:00
|
|
|
</div>
|
2017-12-18 23:08:51 +13:00
|
|
|
</BaseDialog>
|
2016-08-03 11:34:13 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|