diff --git a/monkey/monkey_island/cc/ui/src/components/Main.tsx b/monkey/monkey_island/cc/ui/src/components/Main.tsx index 2b31ae37b..e28013df2 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.tsx +++ b/monkey/monkey_island/cc/ui/src/components/Main.tsx @@ -6,7 +6,6 @@ import ConfigurePage from './pages/ConfigurePage.js'; import RunMonkeyPage from './pages/RunMonkeyPage/RunMonkeyPage'; import MapPage from './pages/MapPage'; import TelemetryPage from './pages/TelemetryPage'; -import StartOverPage from './pages/ResetPage'; import ReportPage from './pages/ReportPage'; import LicensePage from './pages/LicensePage'; import AuthComponent from './AuthComponent'; @@ -47,7 +46,6 @@ export const Routes = { RunMonkeyPage: '/run-monkey', MapPage: '/infection/map', TelemetryPage: '/infection/telemetry', - StartOverPage: '/start-over', LicensePage: '/license' } @@ -232,8 +230,6 @@ class AppComponent extends AuthComponent { )} {this.renderRoute(Routes.TelemetryPage, )} - {this.renderRoute(Routes.StartOverPage, - )} {this.redirectToReport()} {this.renderRoute(Routes.SecurityReport, void } -const SideNavComponent = ({disabled, - completedSteps, - defaultReport, - header=null}: Props) => { +const SideNavComponent = ({ + disabled, + completedSteps, + defaultReport, + header = null, + onStatusChange, + }: Props) => { + + const [showResetModal, setShowResetModal] = useState(false); return ( <> @@ -37,12 +44,12 @@ const SideNavComponent = ({disabled,
    {(header !== null) && - <> -
  • - {header} -
  • -
    - } + <> +
  • + {header} +
  • +
    + }
  • @@ -76,10 +83,17 @@ const SideNavComponent = ({disabled,
  • - + + { + setShowResetModal(false); + onStatusChange(); + }}/>
@@ -90,7 +104,7 @@ const SideNavComponent = ({disabled, Configuration
  • + className={getNavLinkClass()}> Telemetries
  • @@ -98,8 +112,12 @@ const SideNavComponent = ({disabled, ); + function areMonkeysDead() { + return (!completedSteps['runMonkey']) || (completedSteps['infectionDone']) + } + function getNavLinkClass() { - if(disabled){ + if (disabled) { return `nav-link disabled` } else { return '' diff --git a/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx b/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx index c8bac4396..55f22d8b6 100644 --- a/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx +++ b/monkey/monkey_island/cc/ui/src/components/layouts/SidebarLayoutComponent.tsx @@ -9,6 +9,7 @@ const SidebarLayoutComponent = ({component: Component, completedSteps = null, defaultReport = '', sideNavHeader = (<>), + onStatusChange = () => {}, ...other }) => ( { @@ -18,9 +19,10 @@ const SidebarLayoutComponent = ({component: Component, + header={sideNavHeader} + onStatusChange={onStatusChange}/> } - + ) }}/> ) diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx index cc1741b20..d95a84dfe 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx +++ b/monkey/monkey_island/cc/ui/src/components/pages/LandingPage.tsx @@ -12,8 +12,11 @@ import Logo from "../logo/LogoComponent"; const monkeyIcon = require('../../images/monkey-icon.svg') const infectionMonkey = require('../../images/infection-monkey.svg') -const LandingPageComponent = (props) => { +type Props = { + onStatusChange: () => void +} +const LandingPageComponent = (props: Props) => { return ( <> diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js deleted file mode 100644 index 6d0c5bb98..000000000 --- a/monkey/monkey_island/cc/ui/src/components/pages/ResetPage.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; -import {Col, Button} from 'react-bootstrap'; -import {Link} from 'react-router-dom'; -import AuthComponent from '../AuthComponent'; -import IslandResetModal from '../ui-components/IslandResetModal'; -import '../../styles/pages/StartOverPage.scss'; -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; -import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; - -class ResetPageComponent extends AuthComponent { - constructor(props) { - super(props); - - this.state = { - cleaned: false, - showCleanDialog: false, - allMonkeysAreDead: false - }; - - this.cleanup = this.cleanup.bind(this); - this.closeModal = this.closeModal.bind(this); - } - - updateMonkeysRunning = () => { - this.authFetch('/api') - .then(res => res.json()) - .then(res => { - // This check is used to prevent unnecessary re-rendering - this.setState({ - allMonkeysAreDead: (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']) - }); - }); - }; - - render() { - return ( - - -

    Reset

    -
    -

    - If you are finished and want to start over with a fresh configuration, erase the logs and clear the map - you can go ahead and -

    -

    - -

    -
    - - You don't have to reset the environment to keep running monkeys. - You can continue and Run More Monkeys as you wish, - and see the results on the Infection Map without deleting anything. -
    - {this.state.cleaned ? -
    - - Environment was reset successfully -
    - : ''} -
    - - ); - } - - cleanup = () => { - this.setState({ - cleaned: false - }); - return this.authFetch('/api?action=reset') - .then(res => res.json()) - .then(res => { - if (res['status'] === 'OK') { - this.setState({ - cleaned: true - }); - } - }).then(() => { - this.updateMonkeysRunning(); - this.props.onStatusChange(); - }); - }; - - closeModal = () => { - this.setState({ - showCleanDialog: false - }) - }; -} - -export default ResetPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js deleted file mode 100644 index 8639e46a3..000000000 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.js +++ /dev/null @@ -1,76 +0,0 @@ -import {Modal} from 'react-bootstrap'; -import React from 'react'; -import {GridLoader} from 'react-spinners'; -import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; -import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; - - -class IslandResetModal extends React.PureComponent { - - constructor(props) { - super(props); - - this.state = { - showCleanDialog: this.props.showCleanDialog, - allMonkeysAreDead: this.props.allMonkeysAreDead, - loading: false - }; - } - - componentDidUpdate(prevProps) { - if (this.props !== prevProps) { - this.setState({ showCleanDialog: this.props.showCleanDialog, - allMonkeysAreDead: this.props.allMonkeysAreDead}) - } - } - - render = () => { - return ( - this.props.onClose()}> - -

    -
    Reset environment
    -

    -

    - Are you sure you want to reset the environment? -

    - { - !this.state.allMonkeysAreDead ? -
    - - Some monkeys are still running. It's advised to kill all monkeys before resetting. -
    - : -
    - } - { - this.state.loading ?
    : this.showModalButtons() - } - - - ) - }; - - showModalButtons() { - return (
    - - -
    ) - } - - modalVerificationOnClick = async () => { - this.setState({loading: true}); - this.props.onVerify() - .then(() => {this.setState({loading: false}); - this.props.onClose();}) - - } -} - -export default IslandResetModal; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx new file mode 100644 index 000000000..c31d5c401 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/IslandResetModal.tsx @@ -0,0 +1,130 @@ +import {Button, Col, Container, Modal, NavLink, Row} from 'react-bootstrap'; +import React, {useState} from 'react'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle'; + +import '../../styles/components/IslandResetModal.scss'; +import {Routes} from '../Main'; +import LoadingIcon from './LoadingIcon'; +import {faCheck} from '@fortawesome/free-solid-svg-icons'; +import AuthService from '../../services/AuthService'; + +type Props = { + show: boolean, + allMonkeysAreDead: boolean, + onClose: () => void +} + +// Button statuses +const Idle = 1; +const Loading = 2; +const Done = 3; + +const IslandResetModal = (props: Props) => { + + const [resetAllStatus, setResetAll] = useState(Idle); + const [deleteStatus, setDeleteStatus] = useState(Idle); + const auth = new AuthService(); + + return ( + { + setDeleteStatus(Idle); + props.onClose() + }} size={'lg'}> + + Reset the Island + + + { + !props.allMonkeysAreDead ? +
    + + Can't reset the Island while Monkey agents are still running! +
    + : + showModalButtons() + } +
    +
    + ) + + function displayDeleteData() { + if (deleteStatus === Idle) { + return ( + + ) + } else if (deleteStatus === Loading) { + return () + } else if (deleteStatus === Done) { + return () + } + } + + function displayResetAll() { + if (resetAllStatus === Idle) { + return ( + + ) + } else if (resetAllStatus === Loading) { + return () + } else if (resetAllStatus === Done) { + return () + } + } + + function resetIsland(url: string, callback: () => void) { + auth.authFetch(url) + .then(res => res.json()) + .then(res => { + if (res['status'] === 'OK') { + callback() + } + }) + } + + function showModalButtons() { + return ( + + +

    Delete data gathered by Monkey agents.

    +

    This will reset the Map and reports.

    + + + {displayDeleteData()} + +
    +
    + + +

    Reset everything.

    +

    You might want to before doing this.

    + + + {displayResetAll()} + +
    +
    ) + } +} + +export default IslandResetModal; diff --git a/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss b/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss new file mode 100644 index 000000000..2c1c71110 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/components/IslandResetModal.scss @@ -0,0 +1,9 @@ +.island-reset-modal p { + font-size: 16.5px; + margin: 0; + padding-left: 5px; +} + +.island-reset-modal a { + padding: 0 0 4px 0; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss b/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss index 949191d13..078b7daa2 100644 --- a/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss +++ b/monkey/monkey_island/cc/ui/src/styles/components/SideNav.scss @@ -1,6 +1,13 @@ .sidebar .version-text { position: relative; } + .sidebar .license-link { position: relative; } + +.navigation .island-reset-button { + width: 100%; + text-align: left; + padding-left: 18px; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss b/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss deleted file mode 100644 index ee4ab65ea..000000000 --- a/monkey/monkey_island/cc/ui/src/styles/pages/StartOverPage.scss +++ /dev/null @@ -1,9 +0,0 @@ -$yellow: #ffcc00; - -.modalLoader div{ - margin-left: auto; - margin-right: auto; -} -.modalLoader div>div{ - background-color: $yellow; -}