mirror of
https://github.com/pterodactyl/panel.git
synced 2024-11-25 18:42:31 +01:00
Cleanup logic powering totp enabling modal
This commit is contained in:
parent
a4feed24a8
commit
92926ca193
@ -3,16 +3,24 @@ import { useStoreState } from 'easy-peasy';
|
|||||||
import { ApplicationStore } from '@/state';
|
import { ApplicationStore } from '@/state';
|
||||||
import tw from 'twin.macro';
|
import tw from 'twin.macro';
|
||||||
import { Button } from '@/components/elements/button/index';
|
import { Button } from '@/components/elements/button/index';
|
||||||
import SetupTOTPModal from '@/components/dashboard/forms/SetupTOTPModal';
|
|
||||||
import DisableTwoFactorModal from '@/components/dashboard/forms/DisableTwoFactorModal';
|
import DisableTwoFactorModal from '@/components/dashboard/forms/DisableTwoFactorModal';
|
||||||
|
import SetupTOTPModal from '@/components/dashboard/forms/SetupTOTPModal';
|
||||||
|
import RecoveryTokensDialog from '@/components/dashboard/forms/RecoveryTokensDialog';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
const [tokens, setTokens] = useState<string[]>([]);
|
||||||
const [visible, setVisible] = useState<'enable' | 'disable' | null>(null);
|
const [visible, setVisible] = useState<'enable' | 'disable' | null>(null);
|
||||||
const isEnabled = useStoreState((state: ApplicationStore) => state.user.data!.useTotp);
|
const isEnabled = useStoreState((state: ApplicationStore) => state.user.data!.useTotp);
|
||||||
|
|
||||||
|
const onTokens = (tokens: string[]) => {
|
||||||
|
setTokens(tokens);
|
||||||
|
setVisible(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<SetupTOTPModal open={visible === 'enable'} onClose={() => setVisible(null)} />
|
<SetupTOTPModal open={visible === 'enable'} onClose={() => setVisible(null)} onTokens={onTokens} />
|
||||||
|
<RecoveryTokensDialog tokens={tokens} open={tokens.length > 0} onClose={() => setTokens([])} />
|
||||||
<DisableTwoFactorModal visible={visible === 'disable'} onModalDismissed={() => setVisible(null)} />
|
<DisableTwoFactorModal visible={visible === 'disable'} onModalDismissed={() => setVisible(null)} />
|
||||||
<p css={tw`text-sm`}>
|
<p css={tw`text-sm`}>
|
||||||
{isEnabled
|
{isEnabled
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { Dialog, DialogProps } from '@/components/elements/dialog';
|
import { Dialog, DialogWrapperContext } from '@/components/elements/dialog';
|
||||||
import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';
|
import getTwoFactorTokenData, { TwoFactorTokenData } from '@/api/account/getTwoFactorTokenData';
|
||||||
import { useFlashKey } from '@/plugins/useFlash';
|
import { useFlashKey } from '@/plugins/useFlash';
|
||||||
import tw from 'twin.macro';
|
import tw from 'twin.macro';
|
||||||
@ -11,39 +11,28 @@ import CopyOnClick from '@/components/elements/CopyOnClick';
|
|||||||
import Tooltip from '@/components/elements/tooltip/Tooltip';
|
import Tooltip from '@/components/elements/tooltip/Tooltip';
|
||||||
import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';
|
import enableAccountTwoFactor from '@/api/account/enableAccountTwoFactor';
|
||||||
import FlashMessageRender from '@/components/FlashMessageRender';
|
import FlashMessageRender from '@/components/FlashMessageRender';
|
||||||
import RecoveryTokensDialog from '@/components/dashboard/forms/RecoveryTokensDialog';
|
|
||||||
import { Actions, useStoreActions } from 'easy-peasy';
|
import { Actions, useStoreActions } from 'easy-peasy';
|
||||||
import { ApplicationStore } from '@/state';
|
import { ApplicationStore } from '@/state';
|
||||||
|
import asDialog from '@/hoc/asDialog';
|
||||||
|
|
||||||
type SetupTOTPModalProps = DialogProps;
|
interface Props {
|
||||||
|
onTokens: (tokens: string[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export default ({ open, onClose }: SetupTOTPModalProps) => {
|
const ConfigureTwoFactorForm = ({ onTokens }: Props) => {
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
const [tokens, setTokens] = useState<string[]>([]);
|
|
||||||
const [token, setToken] = useState<TwoFactorTokenData | null>(null);
|
const [token, setToken] = useState<TwoFactorTokenData | null>(null);
|
||||||
const { clearAndAddHttpError } = useFlashKey('account:two-step');
|
const { clearAndAddHttpError } = useFlashKey('account:two-step');
|
||||||
const updateUserData = useStoreActions((actions: Actions<ApplicationStore>) => actions.user.updateUserData);
|
const updateUserData = useStoreActions((actions: Actions<ApplicationStore>) => actions.user.updateUserData);
|
||||||
|
|
||||||
useEffect(() => {
|
const { close } = useContext(DialogWrapperContext);
|
||||||
if (!open) return;
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
getTwoFactorTokenData()
|
getTwoFactorTokenData()
|
||||||
.then(setToken)
|
.then(setToken)
|
||||||
.then(() => updateUserData({ useTotp: true }))
|
|
||||||
.catch((error) => clearAndAddHttpError(error));
|
.catch((error) => clearAndAddHttpError(error));
|
||||||
}, [open]);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) return;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setToken(null);
|
|
||||||
setValue('');
|
|
||||||
setSubmitting(false);
|
|
||||||
clearAndAddHttpError(undefined);
|
|
||||||
};
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
if (submitting) return;
|
if (submitting) return;
|
||||||
@ -52,37 +41,26 @@ export default ({ open, onClose }: SetupTOTPModalProps) => {
|
|||||||
clearAndAddHttpError();
|
clearAndAddHttpError();
|
||||||
|
|
||||||
enableAccountTwoFactor(value)
|
enableAccountTwoFactor(value)
|
||||||
.then(setTokens)
|
.then((tokens) => {
|
||||||
.catch(clearAndAddHttpError)
|
updateUserData({ useTotp: true });
|
||||||
.then(() => setSubmitting(false));
|
onTokens(tokens);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
clearAndAddHttpError(error);
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<RecoveryTokensDialog tokens={tokens} open={open && tokens.length > 0} onClose={onClose} />
|
|
||||||
<Dialog
|
|
||||||
open={open && !tokens.length}
|
|
||||||
onClose={onClose}
|
|
||||||
title={'Enable Two-Step Verification'}
|
|
||||||
preventExternalClose={submitting}
|
|
||||||
description={
|
|
||||||
"Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in."
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FlashMessageRender byKey={'account:two-step'} className={'mt-4'} />
|
<FlashMessageRender byKey={'account:two-step'} className={'mt-4'} />
|
||||||
<div
|
<div
|
||||||
className={
|
className={'flex items-center justify-center w-56 h-56 p-2 bg-gray-800 rounded-lg shadow mx-auto mt-6'}
|
||||||
'flex items-center justify-center w-56 h-56 p-2 bg-gray-800 rounded-lg shadow mx-auto mt-6'
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{!token ? (
|
{!token ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : (
|
) : (
|
||||||
<QRCode
|
<QRCode renderAs={'svg'} value={token.image_url_data} css={tw`w-full h-full shadow-none rounded`} />
|
||||||
renderAs={'svg'}
|
|
||||||
value={token.image_url_data}
|
|
||||||
css={tw`w-full h-full shadow-none rounded`}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<CopyOnClick text={token?.secret}>
|
<CopyOnClick text={token?.secret}>
|
||||||
@ -92,8 +70,8 @@ export default ({ open, onClose }: SetupTOTPModalProps) => {
|
|||||||
</CopyOnClick>
|
</CopyOnClick>
|
||||||
<div className={'mt-6'}>
|
<div className={'mt-6'}>
|
||||||
<p>
|
<p>
|
||||||
Scan the QR code above using the two-step authentication app of your choice. Then, enter the
|
Scan the QR code above using the two-step authentication app of your choice. Then, enter the 6-digit
|
||||||
6-digit code generated into the field below.
|
code generated into the field below.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Input.Text
|
<Input.Text
|
||||||
@ -108,12 +86,10 @@ export default ({ open, onClose }: SetupTOTPModalProps) => {
|
|||||||
pattern={'\\d{6}'}
|
pattern={'\\d{6}'}
|
||||||
/>
|
/>
|
||||||
<Dialog.Footer>
|
<Dialog.Footer>
|
||||||
<Button.Text onClick={onClose}>Cancel</Button.Text>
|
<Button.Text onClick={close}>Cancel</Button.Text>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
disabled={value.length === 6}
|
disabled={value.length === 6}
|
||||||
content={
|
content={!token ? 'Waiting for QR code to load...' : 'You must enter the 6-digit code to continue.'}
|
||||||
!token ? 'Waiting for QR code to load...' : 'You must enter the 6-digit code to continue.'
|
|
||||||
}
|
|
||||||
delay={100}
|
delay={100}
|
||||||
>
|
>
|
||||||
<Button disabled={!token || value.length !== 6} onClick={submit}>
|
<Button disabled={!token || value.length !== 6} onClick={submit}>
|
||||||
@ -121,7 +97,12 @@ export default ({ open, onClose }: SetupTOTPModalProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Dialog.Footer>
|
</Dialog.Footer>
|
||||||
</Dialog>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default asDialog({
|
||||||
|
title: 'Enable Two-Step Verification',
|
||||||
|
description:
|
||||||
|
"Help protect your account from unauthorized access. You'll be prompted for a verification code each time you sign in.",
|
||||||
|
})(ConfigureTwoFactorForm);
|
||||||
|
Loading…
Reference in New Issue
Block a user