Compare commits

...

6 Commits

Author SHA1 Message Date
Tudor Stanciu 5fb016a679 [1.2.1] 2023-03-05 02:17:27 +02:00
Tudor Stanciu 06de0a7636 mask machine logs 2023-03-05 02:08:03 +02:00
Tudor Stanciu 6f341415f0 apply mask on sensitive information 2023-03-05 01:39:00 +02:00
Tudor Stanciu d992b5eedc page title translation 2023-03-05 00:52:00 +02:00
Tudor Stanciu 3e9e9534e1 added sensitive info toggle 2023-03-05 00:49:07 +02:00
Tudor Stanciu fea35bab90 [1.2.0] 2023-03-04 23:44:54 +02:00
10 changed files with 476 additions and 345 deletions

654
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "network-resurrector-frontend", "name": "network-resurrector-frontend",
"version": "1.1.2", "version": "1.2.1",
"description": "Frontend component of Network resurrector system", "description": "Frontend component of Network resurrector system",
"author": { "author": {
"name": "Tudor Stanciu", "name": "Tudor Stanciu",

View File

@ -0,0 +1,25 @@
import React from "react";
import { IconButton } from "@material-ui/core";
import {
Visibility as VisibilityIcon,
VisibilityOff as VisibilityOffIcon
} from "@material-ui/icons";
import { useSensitiveInfo } from "../../hooks";
const SensitiveInfoToggle = () => {
const { enabled, onSensitiveInfoEnabled } = useSensitiveInfo();
const handleChange = () => onSensitiveInfoEnabled(!enabled);
return (
<IconButton
aria-label="sensitive-info-toggle"
color="inherit"
onClick={handleChange}
>
{enabled ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton>
);
};
export default SensitiveInfoToggle;

View File

@ -6,6 +6,7 @@ import { AppBar, Toolbar, Typography, IconButton } from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu"; import MenuIcon from "@material-ui/icons/Menu";
import ProfileButton from "./ProfileButton"; import ProfileButton from "./ProfileButton";
import LightDarkToggle from "./LightDarkToggle"; import LightDarkToggle from "./LightDarkToggle";
import SensitiveInfoToggle from "./SensitiveInfoToggle";
import styles from "./styles"; import styles from "./styles";
const useStyles = makeStyles(styles); const useStyles = makeStyles(styles);
@ -35,6 +36,7 @@ const TopBar = ({ open, handleDrawerOpen }) => {
<Typography variant="h6" noWrap className={classes.title}> <Typography variant="h6" noWrap className={classes.title}>
Network resurrector Network resurrector
</Typography> </Typography>
<SensitiveInfoToggle />
<LightDarkToggle /> <LightDarkToggle />
<ProfileButton /> <ProfileButton />
</Toolbar> </Toolbar>

View File

@ -12,6 +12,7 @@ import { KeyboardArrowDown, KeyboardArrowUp } from "@material-ui/icons";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import MachineLog from "./MachineLog"; import MachineLog from "./MachineLog";
import WakeComponent from "./WakeComponent"; import WakeComponent from "./WakeComponent";
import { useSensitiveInfo } from "../../../hooks";
const useRowStyles = makeStyles({ const useRowStyles = makeStyles({
root: { root: {
@ -62,6 +63,7 @@ const Machine = ({
}) => { }) => {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const classes = useRowStyles(); const classes = useRowStyles();
const { mask, maskElements } = useSensitiveInfo();
const topActions = useMemo( const topActions = useMemo(
() => actions.filter(a => a.top === true), () => actions.filter(a => a.top === true),
@ -86,11 +88,11 @@ const Machine = ({
</IconButton> </IconButton>
</TableCell> </TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
{machine.fullMachineName} {mask(machine.fullMachineName)}
</TableCell> </TableCell>
<TableCell>{machine.machineName}</TableCell> <TableCell>{mask(machine.machineName)}</TableCell>
<TableCell>{machine.iPv4Address}</TableCell> <TableCell>{mask(machine.iPv4Address)}</TableCell>
<TableCell>{machine.macAddress}</TableCell> <TableCell>{mask(machine.macAddress)}</TableCell>
<TableCell align="right"> <TableCell align="right">
<WakeComponent machine={machine} addLog={addLog} /> <WakeComponent machine={machine} addLog={addLog} />
{topActions.map(action => ( {topActions.map(action => (
@ -120,7 +122,7 @@ const Machine = ({
<TableRow> <TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}> <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
<MachineLog logs={logs} /> <MachineLog logs={maskElements(logs)} />
</Collapse> </Collapse>
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@ -6,10 +6,12 @@ import {
import useApi from "../../../api"; import useApi from "../../../api";
import MachinesList from "./MachinesList"; import MachinesList from "./MachinesList";
import PageTitle from "../../../components/common/PageTitle"; import PageTitle from "../../../components/common/PageTitle";
import { useTranslation } from "react-i18next";
const MachinesContainer = () => { const MachinesContainer = () => {
const state = useContext(ApplicationStateContext); const state = useContext(ApplicationStateContext);
const dispatchActions = useContext(ApplicationDispatchContext); const dispatchActions = useContext(ApplicationDispatchContext);
const { t } = useTranslation();
const api = useApi(); const api = useApi();
@ -30,7 +32,7 @@ const MachinesContainer = () => {
return ( return (
<> <>
<PageTitle text="Machines" /> <PageTitle text={t("Menu.Machines")} />
<MachinesList dense={true} machines={state.network.machines} /> <MachinesList dense={true} machines={state.network.machines} />
</> </>
); );

View File

@ -1 +1,4 @@
export { useToast } from "./useToast"; import { useToast } from "./useToast";
import { useSensitiveInfo } from "../providers/SensitiveInfoProvider";
export { useToast, useSensitiveInfo };

View File

@ -6,20 +6,23 @@ import App from "./components/App";
import { TuitioProvider } from "@flare/tuitio-client-react"; import { TuitioProvider } from "@flare/tuitio-client-react";
import ApplicationStateProvider from "./providers/ApplicationStateProvider"; import ApplicationStateProvider from "./providers/ApplicationStateProvider";
import ToastProvider from "./providers/ToastProvider"; import ToastProvider from "./providers/ToastProvider";
import SensitiveInfoProvider from "./providers/SensitiveInfoProvider";
import "./utils/i18n"; import "./utils/i18n";
ReactDOM.render( ReactDOM.render(
<TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}> <TuitioProvider tuitioUrl={process.env.REACT_APP_TUITIO_URL}>
<ApplicationStateProvider> <ThemeProvider>
<ThemeProvider> <CssBaseline />
<CssBaseline /> <SensitiveInfoProvider>
<Suspense fallback={<div>Loading...</div>}> <ApplicationStateProvider>
<ToastProvider> <Suspense fallback={<div>Loading...</div>}>
<App /> <ToastProvider>
</ToastProvider> <App />
</Suspense> </ToastProvider>
</ThemeProvider> </Suspense>
</ApplicationStateProvider> </ApplicationStateProvider>
</SensitiveInfoProvider>
</ThemeProvider>
</TuitioProvider>, </TuitioProvider>,
document.getElementById("root") document.getElementById("root")
); );

View File

@ -0,0 +1,70 @@
import React, { useReducer, useMemo, useContext } from "react";
import PropTypes from "prop-types";
import { obfuscate } from "../utils/obfuscateStrings";
const SensitiveInfoContext = React.createContext();
const initialState = {
enabled: false
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case "onSensitiveInfoEnabled": {
return {
...state,
enabled: action.payload.enabled
};
}
default: {
return state;
}
}
};
const dispatchActions = dispatch => ({
onSensitiveInfoEnabled: enabled =>
dispatch({ type: "onSensitiveInfoEnabled", payload: { enabled } })
});
const useSensitiveInfo = () => {
const { state, actions } = useContext(SensitiveInfoContext);
const { enabled } = state;
const { onSensitiveInfoEnabled } = actions;
const mask = text => {
if (!enabled) return text;
return obfuscate(text, "#");
};
const maskElements = list => {
if (!enabled) return list;
const maskedList = list.map(z => obfuscate(z, "#"));
return maskedList;
};
return { enabled, onSensitiveInfoEnabled, mask, maskElements };
};
const SensitiveInfoProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const actions = useMemo(() => dispatchActions(dispatch), [dispatch]);
return (
<SensitiveInfoContext.Provider
value={{
state,
actions
}}
>
{children}
</SensitiveInfoContext.Provider>
);
};
SensitiveInfoProvider.propTypes = {
children: PropTypes.node.isRequired
};
export { SensitiveInfoProvider, useSensitiveInfo };
export default SensitiveInfoProvider;

View File

@ -0,0 +1,24 @@
const obfuscateForChars = (text, placeholder = "*") => {
const firstChar = text.substring(0, 1);
const lastChar = text.substring(text.length - 1);
const middleChars = text
.substring(1, text.length - 1)
.replace(/[a-zA-Z0-9]/g, placeholder);
return firstChar + middleChars + lastChar;
};
const obfuscate = (text, placeholder = "*") => {
if (text.length <= 2) return text;
if (text.length <= 5) {
return obfuscateForChars(text);
}
const firstTwoChars = text.substring(0, 2);
const lastChar = text.substring(text.length - 1);
const middleChars = text
.substring(2, text.length - 1)
.replace(/[a-zA-Z0-9]/g, placeholder);
return firstTwoChars + middleChars + lastChar;
};
export { obfuscate };