// Copyright 2023 The Casdoor Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import React, {useState} from "react"; import {Button, Col, Form, Input, Result, Row, Steps} from "antd"; import * as Setting from "../Setting"; import i18next from "i18next"; import * as MfaBackend from "../backend/MfaBackend"; import {CheckOutlined, KeyOutlined, LockOutlined, UserOutlined} from "@ant-design/icons"; import * as UserBackend from "../backend/UserBackend"; import {MfaSmsVerifyForm, MfaTotpVerifyForm} from "./MfaVerifyForm"; import * as ApplicationBackend from "../backend/ApplicationBackend"; const {Step} = Steps; export const SmsMfaType = "sms"; export const TotpMfaType = "app"; function CheckPasswordForm({user, onSuccess, onFail}) { const [form] = Form.useForm(); const onFinish = ({password}) => { const data = {...user, password}; UserBackend.checkUserPassword(data) .then((res) => { if (res.status === "ok") { onSuccess(res); } else { onFail(res); } }) .finally(() => { form.setFieldsValue({password: ""}); }); }; return (
} placeholder={i18next.t("general:Password")} />
); } export function MfaVerifyForm({mfaProps, application, user, onSuccess, onFail}) { const [form] = Form.useForm(); const onFinish = ({passcode}) => { const data = {passcode, type: mfaProps.type, ...user}; MfaBackend.MfaSetupVerify(data) .then((res) => { if (res.status === "ok") { onSuccess(res); } else { onFail(res); } }) .catch((error) => { Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`); }) .finally(() => { form.setFieldsValue({passcode: ""}); }); }; if (mfaProps.type === SmsMfaType) { return ; } else if (mfaProps.type === TotpMfaType) { return ; } else { return
; } } function EnableMfaForm({user, mfaProps, onSuccess, onFail}) { const [loading, setLoading] = useState(false); const requestEnableTotp = () => { const data = { type: mfaProps.type, ...user, }; setLoading(true); MfaBackend.MfaSetupEnable(data).then(res => { if (res.status === "ok") { onSuccess(res); } else { onFail(res); } } ).finally(() => { setLoading(false); }); }; return (

{i18next.t("mfa:Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code")}


{mfaProps.recoveryCodes[0]}
); } class MfaSetupPage extends React.Component { constructor(props) { super(props); this.state = { account: props.account, current: 0, type: props.type ?? SmsMfaType, mfaProps: null, }; } componentDidMount() { this.getApplication(); } getApplication() { ApplicationBackend.getApplication("admin", this.state.account.signupApplication) .then((application) => { if (application !== null) { this.setState({ application: application, }); } else { Setting.showMessage("error", i18next.t("mfa:Failed to get application")); } }); } getUser() { return { name: this.state.account.name, owner: this.state.account.owner, }; } renderStep() { switch (this.state.current) { case 0: return { MfaBackend.MfaSetupInitiate({ type: this.state.type, ...this.getUser(), }).then((res) => { if (res.status === "ok") { this.setState({ current: this.state.current + 1, mfaProps: res.data, }); } else { Setting.showMessage("error", i18next.t("mfa:Failed to initiate MFA")); } }); }} onFail={(res) => { Setting.showMessage("error", i18next.t("mfa:Failed to initiate MFA")); }} />; case 1: return { this.setState({ current: this.state.current + 1, }); }} onFail={(res) => { Setting.showMessage("error", i18next.t("general:Failed to verify")); }} />; case 2: return { Setting.showMessage("success", i18next.t("general:Enabled successfully")); Setting.goToLinkSoft(this, "/account"); }} onFail={(res) => { Setting.showMessage("error", `${i18next.t("general:Failed to enable")}: ${res.msg}`); }} />; default: return null; } } render() { if (!this.props.account) { return ( } /> ); } return (
{i18next.t("mfa:Protect your account with Multi-factor authentication")}
{i18next.t("mfa:Each time you sign in to your Account, you'll need your password and a authentication code")}
} /> } /> } />
{this.renderStep()}
); } } export default MfaSetupPage;