[staff] Add option for toggling email 2fa
This commit is contained in:
@@ -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}`,
|
||||
},
|
||||
};
|
||||
|
||||
183
infra/staff/src/components/ToggleEmailMFA.tsx
Normal file
183
infra/staff/src/components/ToggleEmailMFA.tsx
Normal file
@@ -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<ToggleEmailMFAProps> = ({
|
||||
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 (
|
||||
<div>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
PaperComponent={Paper}
|
||||
sx={{
|
||||
width: "499px",
|
||||
height: "286px",
|
||||
margin: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
BackdropProps={{
|
||||
style: {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.9)", // Semi-transparent backdrop
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{"Toggle Email MFA"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
Do you want to enable or disable Email MFA for this account?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ justifyContent: "center" }}>
|
||||
<Button
|
||||
onClick={handleCancel}
|
||||
sx={{
|
||||
bgcolor: "white",
|
||||
color: "black",
|
||||
"&:hover": { bgcolor: "#FAFAFA" },
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleToggle(true).catch((error: unknown) =>
|
||||
console.error(error),
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
bgcolor: "#4CAF50",
|
||||
color: "white",
|
||||
"&:hover": { bgcolor: "#45A049" },
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Processing..." : "Enable Email MFA"}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleToggle(false).catch((error: unknown) =>
|
||||
console.error(error),
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
bgcolor: "#F4473D",
|
||||
color: "white",
|
||||
"&:hover": { bgcolor: "#E53935" },
|
||||
}}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Processing..." : "Disable Email MFA"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToggleEmailMFA;
|
||||
@@ -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<string, string>;
|
||||
@@ -32,8 +33,11 @@ interface UserComponentProps {
|
||||
|
||||
const UserComponent: React.FC<UserComponentProps> = ({ 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<UserComponentProps> = ({ 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<UserComponentProps> = ({ userData }) => {
|
||||
onDeleteAccount={handleDeleteAccountClick}
|
||||
onEditSubscription={handleEditSubscription}
|
||||
onDisablePasskeys={handleDisablePasskeys}
|
||||
canDisableEmailMFA={canDisableEmailMFA}
|
||||
twoFactorEnabled={twoFactorEnabled}
|
||||
setTwoFactorEnabled={setTwoFactorEnabled}
|
||||
setDisable2FAOpen={setDisable2FAOpen}
|
||||
email2FAEnabled={email2FAEnabled}
|
||||
setEmail2FAToggleOpen={setEmail2FAToggleOpen}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
@@ -78,6 +87,11 @@ const UserComponent: React.FC<UserComponentProps> = ({ userData }) => {
|
||||
handleClose={() => setDisable2FAOpen(false)}
|
||||
handleDisable2FA={() => setTwoFactorEnabled(false)}
|
||||
/>
|
||||
<ToggleEmailMFA
|
||||
open={email2FAOpen}
|
||||
handleClose={() => setEmail2FAToggleOpen(false)}
|
||||
handleToggleEmailMFA={(status) => setEmail2FAToggleOpen(status)}
|
||||
/>
|
||||
<UpdateSubscription
|
||||
open={updateSubscriptionOpen}
|
||||
onClose={() => 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<DataTableProps> = ({
|
||||
@@ -114,9 +131,12 @@ const DataTable: React.FC<DataTableProps> = ({
|
||||
onDeleteAccount,
|
||||
onEditSubscription,
|
||||
onDisablePasskeys,
|
||||
canDisableEmailMFA,
|
||||
twoFactorEnabled,
|
||||
setTwoFactorEnabled,
|
||||
setDisable2FAOpen,
|
||||
email2FAEnabled,
|
||||
setEmail2FAToggleOpen,
|
||||
}) => (
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
@@ -186,7 +206,9 @@ const DataTable: React.FC<DataTableProps> = ({
|
||||
aria-label={title}
|
||||
>
|
||||
<TableBody>
|
||||
{Object.entries(data).map(([label, value], index) => (
|
||||
{Object.entries(data)
|
||||
.filter(([label]) => label !== "Can Disable EmailMFA")
|
||||
.map(([label, value], index) => (
|
||||
<TableRow key={label}>
|
||||
<TableCell
|
||||
component="th"
|
||||
@@ -217,9 +239,12 @@ const DataTable: React.FC<DataTableProps> = ({
|
||||
value,
|
||||
onEditEmail,
|
||||
onDisablePasskeys,
|
||||
canDisableEmailMFA,
|
||||
twoFactorEnabled,
|
||||
setTwoFactorEnabled,
|
||||
setDisable2FAOpen,
|
||||
email2FAEnabled,
|
||||
setEmail2FAToggleOpen,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -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 = (
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
case "Email MFA":
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "right",
|
||||
width: "100%",
|
||||
paddingRight: "50px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ marginRight: "1px" }}>{value}</Typography>
|
||||
{canToggleEmailMFA && (
|
||||
<Switch
|
||||
checked={email2FAEnabled}
|
||||
onChange={(e) => {
|
||||
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",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
default:
|
||||
return <Typography>{value}</Typography>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user