From 4e1bc124ffa23c972a043cfe48071fa57f62c165 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:49:52 +0530 Subject: [PATCH] [staff] Add option for toggling email 2fa --- infra/staff/src/App.tsx | 2 + infra/staff/src/components/ToggleEmailMFA.tsx | 183 ++++++++++++++++++ infra/staff/src/components/UserComponent.tsx | 65 ++++++- 3 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 infra/staff/src/components/ToggleEmailMFA.tsx diff --git a/infra/staff/src/App.tsx b/infra/staff/src/App.tsx index 5c35251ded..0586b69dff 100644 --- a/infra/staff/src/App.tsx +++ b/infra/staff/src/App.tsx @@ -47,6 +47,7 @@ interface Security { isTwoFactorEnabled: boolean; passkeys: string; passkeyCount: number; + canDisableEmailMFA: boolean; } interface UserResponse { @@ -189,6 +190,7 @@ const App: React.FC = () => { 0) > 0 ? "Enabled" : "Disabled", + "Can Disable EmailMFA": userDataResponse.details?.profileData.canDisableEmailMFA ? "Yes":"No", AuthCodes: `${userDataResponse.authCodes ?? 0}`, }, }; diff --git a/infra/staff/src/components/ToggleEmailMFA.tsx b/infra/staff/src/components/ToggleEmailMFA.tsx new file mode 100644 index 0000000000..919c6002ec --- /dev/null +++ b/infra/staff/src/components/ToggleEmailMFA.tsx @@ -0,0 +1,183 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Paper, +} from "@mui/material"; +import React, { useState } from "react"; +import { getEmail, getToken } from "../App"; // Import getEmail and getToken functions +import { apiOrigin } from "../services/support"; + +interface UserData { + subscription?: { + userID: string; + // Add other properties as per your API response structure + }; + // Add other properties as per your API response structure +} + +interface ToggleEmailMFAProps { + open: boolean; + handleClose: () => void; + handleToggleEmailMFA: (status: boolean) => void; // Callback to handle toggling Email MFA +} + +const ToggleEmailMFA: React.FC = ({ + open, + handleClose, + handleToggleEmailMFA, +}) => { + const [loading, setLoading] = useState(false); + + const handleToggle = async (enable: boolean) => { + try { + setLoading(true); + const email = getEmail(); + const token = getToken(); + + if (!email) { + throw new Error("Email not found"); + } + + if (!token) { + throw new Error("Token not found"); + } + + const encodedEmail = encodeURIComponent(email); + + // Fetch user data + const userUrl = `${apiOrigin}/admin/user?email=${encodedEmail}`; + const userResponse = await fetch(userUrl, { + method: "GET", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": token, + }, + }); + if (!userResponse.ok) { + throw new Error("Failed to fetch user data"); + } + const userData = (await userResponse.json()) as UserData; + const userId = userData.subscription?.userID; + + if (!userId) { + throw new Error("User ID not found"); + } + + // Toggle Email MFA action + const toggleEmailMFAUrl = `${apiOrigin}/admin/user/update-email-mfa`; + const body = JSON.stringify({ userID: userId, emailMFA: enable }); + const toggleEmailMFAResponse = await fetch(toggleEmailMFAUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Auth-Token": token, + }, + body: body, + }); + + if (!toggleEmailMFAResponse.ok) { + const errorResponse = await toggleEmailMFAResponse.text(); + throw new Error(`Failed to update Email MFA: ${errorResponse}`); + } + + handleToggleEmailMFA(enable); // Notify parent component of successful action with status + handleClose(); // Close dialog on successful action + console.log(`Email MFA ${enable ? "enabled" : "disabled"} successfully`); + } catch (error) { + if (error instanceof Error) { + alert(error.message); + } else { + alert("Failed to update Email MFA"); + } + } finally { + setLoading(false); + } + }; + + const handleCancel = () => { + handleClose(); // Close dialog + }; + + return ( +
+ + + {"Toggle Email MFA"} + + + + Do you want to enable or disable Email MFA for this account? + + + + + + + + +
+ ); +}; + +export default ToggleEmailMFA; \ No newline at end of file diff --git a/infra/staff/src/components/UserComponent.tsx b/infra/staff/src/components/UserComponent.tsx index 85265fa2d8..f0c78b831f 100644 --- a/infra/staff/src/components/UserComponent.tsx +++ b/infra/staff/src/components/UserComponent.tsx @@ -18,6 +18,7 @@ import DeleteAccount from "./DeleteAccont"; import Disable2FA from "./Disable2FA"; import DisablePasskeys from "./DisablePasskeys"; import UpdateSubscription from "./UpdateSubscription"; +import ToggleEmailMFA from "./ToggleEmailMFA"; export interface UserData { User: Record; @@ -32,8 +33,11 @@ interface UserComponentProps { const UserComponent: React.FC = ({ userData }) => { const [deleteAccountOpen, setDeleteAccountOpen] = React.useState(false); + const [email2FAEnabled, setEmail2FAEnabled] = React.useState(false); + const [email2FAOpen, setEmail2FAToggleOpen] = React.useState(false); const [disable2FAOpen, setDisable2FAOpen] = React.useState(false); const [twoFactorEnabled, setTwoFactorEnabled] = React.useState(false); + const [canDisableEmailMFA, setCanDisableEmailMFA] = React.useState(false); const [updateSubscriptionOpen, setUpdateSubscriptionOpen] = React.useState(false); const [changeEmailOpen, setChangeEmailOpen] = React.useState(false); @@ -41,6 +45,8 @@ const UserComponent: React.FC = ({ userData }) => { React.useEffect(() => { setTwoFactorEnabled(userData?.Security["Two factor 2FA"] === "Enabled"); + setEmail2FAEnabled(userData?.Security["Email MFA"] === "Enabled"); + setCanDisableEmailMFA(userData?.Security["Can Disable EmailMFA"] === "Yes"); }, [userData]); const handleEditEmail = () => setChangeEmailOpen(true); @@ -62,9 +68,12 @@ const UserComponent: React.FC = ({ userData }) => { onDeleteAccount={handleDeleteAccountClick} onEditSubscription={handleEditSubscription} onDisablePasskeys={handleDisablePasskeys} + canDisableEmailMFA={canDisableEmailMFA} twoFactorEnabled={twoFactorEnabled} setTwoFactorEnabled={setTwoFactorEnabled} setDisable2FAOpen={setDisable2FAOpen} + email2FAEnabled={email2FAEnabled} + setEmail2FAToggleOpen={setEmail2FAToggleOpen} /> ))} @@ -78,6 +87,11 @@ const UserComponent: React.FC = ({ userData }) => { handleClose={() => setDisable2FAOpen(false)} handleDisable2FA={() => setTwoFactorEnabled(false)} /> + setEmail2FAToggleOpen(false)} + handleToggleEmailMFA={(status) => setEmail2FAToggleOpen(status)} + /> setUpdateSubscriptionOpen(false)} @@ -102,9 +116,12 @@ interface DataTableProps { onDeleteAccount: () => void; onEditSubscription: () => void; onDisablePasskeys: () => void; + canDisableEmailMFA: boolean; twoFactorEnabled: boolean; setTwoFactorEnabled: (enabled: boolean) => void; setDisable2FAOpen: (open: boolean) => void; + email2FAEnabled: boolean; + setEmail2FAToggleOpen: (open: boolean) => void; } const DataTable: React.FC = ({ @@ -114,9 +131,12 @@ const DataTable: React.FC = ({ onDeleteAccount, onEditSubscription, onDisablePasskeys, + canDisableEmailMFA, twoFactorEnabled, setTwoFactorEnabled, setDisable2FAOpen, + email2FAEnabled, + setEmail2FAToggleOpen, }) => ( = ({ aria-label={title} > - {Object.entries(data).map(([label, value], index) => ( + {Object.entries(data) + .filter(([label]) => label !== "Can Disable EmailMFA") + .map(([label, value], index) => ( = ({ value, onEditEmail, onDisablePasskeys, + canDisableEmailMFA, twoFactorEnabled, setTwoFactorEnabled, setDisable2FAOpen, + email2FAEnabled, + setEmail2FAToggleOpen, )} @@ -234,9 +259,12 @@ const renderTableCellContent = ( value: string, onEditEmail: () => void, onDisablePasskeys: () => void, + canToggleEmailMFA: boolean, twoFactorEnabled: boolean, setTwoFactorEnabled: (enabled: boolean) => void, setDisable2FAOpen: (open: boolean) => void, + email2FAEnabled: boolean, + setEmail2FAToggleOpen: (open: boolean) => void, ) => { switch (label) { case "Email": @@ -307,6 +335,41 @@ const renderTableCellContent = ( )} ); + case "Email MFA": + return ( + + {value} + {canToggleEmailMFA && ( + { + setEmail2FAToggleOpen(true); + }} + sx={{ + "& .MuiSwitch-switchBase.Mui-checked": { + color: "#00B33C", + "&:hover": { + backgroundColor: + "rgba(0, 179, 60, 0.08)", + }, + }, + "& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": + { + backgroundColor: "#00B33C", + }, + }} + /> + )} + + ); default: return {value}; }