From c4f4a8ef95f9f3d586e261b1f3480874aaf78b3f Mon Sep 17 00:00:00 2001
From: VakarisZ
Date: Fri, 18 Sep 2020 15:11:26 +0300
Subject: [PATCH] Refactored UI of run monkey page
---
.../RunMonkeyPage/LocalManualRunOptions.js | 1 -
.../pages/RunMonkeyPage/RemoteRunOptions.js | 30 --
.../pages/RunMonkeyPage/RunMonkeyPage.js | 339 +------------
.../pages/RunMonkeyPage/RunMonkeyPage2.js | 472 ------------------
.../pages/RunMonkeyPage/RunOnIslandButton.js | 113 +++++
.../{ManualRunOptions.js => RunOptions.js} | 26 +-
.../inline-selection/InlineSelection.js | 2 +-
.../inline-selection/NextSelectionButton.js | 5 +-
.../monkey_island/cc/ui/src/styles/Main.scss | 1 +
.../cc/ui/src/styles/components/Icons.scss | 13 +
.../styles/components/RunOnIslandButton.scss | 7 +
11 files changed, 150 insertions(+), 859 deletions(-)
delete mode 100644 monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RemoteRunOptions.js
delete mode 100644 monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js
create mode 100644 monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js
rename monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/{ManualRunOptions.js => RunOptions.js} (66%)
create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/Icons.scss
create mode 100644 monkey/monkey_island/cc/ui/src/styles/components/RunOnIslandButton.scss
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js
index d5fe77558..0ea045d87 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/LocalManualRunOptions.js
@@ -1,6 +1,5 @@
import React, {useEffect, useState} from 'react';
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
-import ManualRunOptions from './ManualRunOptions';
import DropdownSelect from '../../ui-components/DropdownSelect';
import {OS_TYPES} from './OsTypes';
import GenerateLocalWindowsCmd from './commands/local_windows_cmd';
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RemoteRunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RemoteRunOptions.js
deleted file mode 100644
index e1731aba4..000000000
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RemoteRunOptions.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React, {useEffect, useState} from 'react';
-import NextSelectionButton from '../../ui-components/inline-selection/NextSelectionButton';
-import LocalManualRunOptions from './LocalManualRunOptions';
-import {faLaptopCode} from '@fortawesome/free-solid-svg-icons/faLaptopCode';
-import {faNetworkWired} from '@fortawesome/free-solid-svg-icons/faNetworkWired';
-import {faCogs} from '@fortawesome/free-solid-svg-icons/faCogs';
-import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
-import ManualRunOptions from './ManualRunOptions';
-
-
-const RemoteRunOptions = (props) => {
- return InlineSelection(getContents, props)
-}
-
-function getContents() {
- return (
- <>
- {
- setComponent(LocalManualRunOptions,
- {ips: ips, setComponent: setComponent})
- }}/>
- >
- );
-}
-
-
-export default ManualRunOptions;
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage.js
index 1a01ac913..2a27c5be3 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage.js
@@ -1,288 +1,12 @@
import React from 'react';
-import {css} from '@emotion/core';
-import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
-import GridLoader from 'react-spinners/GridLoader';
-
-import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
-import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
-import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
-import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
-import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
-
+import {Col} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import AuthComponent from '../../AuthComponent';
-import AwsRunTable from '../../run-monkey/AwsRunTable';
+import RunOptions from './RunOptions';
-import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
-import ManualRunOptions from './ManualRunOptions';
-import Emoji from '../../ui-components/Emoji';
-
-const loading_css_override = css`
- display: block;
- margin-right: auto;
- margin-left: auto;
-`;
class RunMonkeyPageComponent extends AuthComponent {
- constructor(props) {
- super(props);
- this.state = {
- runningOnIslandState: 'not_running',
- runningOnClientState: 'not_running',
- awsClicked: false,
- showManual: false,
- showAws: false,
- isOnAws: false,
- awsUpdateClicked: false,
- awsUpdateFailed: false,
- awsMachines: [],
- isLoadingAws: true,
- isErrorWhileCollectingAwsMachines: false,
- awsMachineCollectionErrorMsg: '',
- showModal: false,
- errorDetails: ''
- };
-
- this.closeModal = this.closeModal.bind(this);
- }
-
- componentDidMount() {
- this.authFetch('/api/local-monkey')
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({runningOnIslandState: 'running'});
- } else {
- this.setState({runningOnIslandState: 'not_running'});
- }
- });
-
- this.fetchAwsInfo();
- this.fetchConfig();
-
- this.authFetch('/api/client-monkey')
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({runningOnClientState: 'running'});
- } else {
- this.setState({runningOnClientState: 'not_running'});
- }
- });
-
- this.props.onStatusChange();
- }
-
- fetchAwsInfo() {
- return this.authFetch('/api/remote-monkey?action=list_aws')
- .then(res => res.json())
- .then(res => {
- let is_aws = res['is_aws'];
- if (is_aws) {
- // On AWS!
- // Checks if there was an error while collecting the aws machines.
- let is_error_while_collecting_aws_machines = (res['error'] != null);
- if (is_error_while_collecting_aws_machines) {
- // There was an error. Finish loading, and display error message.
- this.setState({
- isOnAws: true,
- isErrorWhileCollectingAwsMachines: true,
- awsMachineCollectionErrorMsg: res['error'],
- isLoadingAws: false
- });
- } else {
- // No error! Finish loading and display machines for user
- this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false});
- }
- } else {
- // Not on AWS. Finish loading and don't display the AWS div.
- this.setState({isOnAws: false, isLoadingAws: false});
- }
- });
- }
-
- runIslandMonkey = () => {
- this.authFetch('/api/local-monkey',
- {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({action: 'run'})
- })
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({
- runningOnIslandState: 'installing'
- });
- } else {
- /* If Monkey binaries are missing, change the state accordingly */
- if (res['error_text'].startsWith('Copy file failed')) {
- this.setState({
- showModal: true,
- errorDetails: res['error_text']
- }
- );
- }
- this.setState({
- runningOnIslandState: 'not_running'
- });
- }
-
- this.props.onStatusChange();
- });
- };
-
- static renderIconByState(state) {
- if (state === 'running') {
- return ( )
- } else if (state === 'installing') {
- return ( )
- } else {
- return '';
- }
- }
-
- toggleManual = () => {
- this.setState({
- showManual: !this.state.showManual
- });
- };
-
- toggleAws = () => {
- this.setState({
- showAws: !this.state.showAws
- });
- };
-
- runOnAws = () => {
- this.setState({
- awsClicked: true
- });
-
- let instances = this.awsTable.state.selection.map(x => this.instanceIdToInstance(x));
-
- this.authFetch('/api/remote-monkey',
- {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({type: 'aws', instances: instances, island_ip: this.state.selectedIp})
- }).then(res => res.json())
- .then(res => {
- let result = res['result'];
-
- // update existing state, not run-over
- let prevRes = this.awsTable.state.result;
- for (let key in result) {
- if (result.hasOwnProperty(key)) {
- prevRes[key] = result[key];
- }
- }
- this.awsTable.setState({
- result: prevRes,
- selection: [],
- selectAll: false
- });
-
- this.setState({
- awsClicked: false
- });
- });
- };
-
- fetchConfig() {
- return this.authFetch('/api/configuration/island')
- .then(res => res.json())
- .then(res => {
- return res.configuration;
- })
- }
-
- instanceIdToInstance = (instance_id) => {
- let instance = this.state.awsMachines.find(
- function (inst) {
- return inst['instance_id'] === instance_id;
- });
- return {'instance_id': instance_id, 'os': instance['os']}
-
- };
-
- renderAwsMachinesDiv() {
- return (
-
-
-
-
(this.awsTable = r)}
- />
-
-
- Run on selected machines
- {this.state.awsClicked ?
- : null}
-
-
-
- )
- }
-
- closeModal = () => {
- this.setState({
- showModal: false
- })
- };
-
- renderIslandVsManual = () => {
- return (
- <>
-
-
- Run on Monkey Island Server
- {RunMonkeyPageComponent.renderIconByState(this.state.runningOnIslandState)}
-
-
-
-
- OR
-
-
-
- Run on a machine of your choice
-
-
-
- Go ahead and monitor the ongoing infection in the Infection Map view.
-
- >
- )
- }
-
- disableManualOptions = () => {
- this.setState({showManual: false})
- }
-
render() {
return (
(Or configure the monkey to fine tune its behavior)
- {this.state.showManual ?
- :
- this.renderIslandVsManual()}
- {
- this.state.isLoadingAws ?
-
- : null
- }
- {
- this.state.isOnAws ?
-
- OR
-
- :
- null
- }
- {
- this.state.isOnAws ?
-
-
- Run on AWS machine of your choice
-
-
- :
- null
- }
-
- {
- this.state.isErrorWhileCollectingAwsMachines ?
-
-
-
- Error while collecting AWS machine data. Error
- message: {this.state.awsMachineCollectionErrorMsg}
- Are you sure you've set the correct role on your Island AWS machine?
- Not sure what this is? Read
- the documentation !
-
-
- :
- this.renderAwsMachinesDiv()
- }
-
-
+
);
}
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js
deleted file mode 100644
index 8edd0e44e..000000000
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunMonkeyPage2.js
+++ /dev/null
@@ -1,472 +0,0 @@
-import React from 'react';
-import {css} from '@emotion/core';
-import {Button, Col, Card, Nav, Collapse, Row} from 'react-bootstrap';
-import CopyToClipboard from 'react-copy-to-clipboard';
-import GridLoader from 'react-spinners/GridLoader';
-
-import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
-import {faClipboard} from '@fortawesome/free-solid-svg-icons/faClipboard';
-import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
-import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
-import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle';
-import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
-
-import {Link} from 'react-router-dom';
-import AuthComponent from '../../AuthComponent';
-import AwsRunTable from '../../run-monkey/AwsRunTable';
-
-import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
-import ManualRunOptions from './ManualRunOptions';
-import Emoji from '../../ui-components/Emoji';
-
-const loading_css_override = css`
- display: block;
- margin-right: auto;
- margin-left: auto;
-`;
-
-class RunMonkeyPageComponent2 extends AuthComponent {
-
- constructor(props) {
- super(props);
- this.state = {
- ips: [],
- runningOnIslandState: 'not_running',
- runningOnClientState: 'not_running',
- awsClicked: false,
- selectedIp: '0.0.0.0',
- selectedOs: 'windows-32',
- showManual: false,
- showAws: false,
- isOnAws: false,
- awsUpdateClicked: false,
- awsUpdateFailed: false,
- awsMachines: [],
- isLoadingAws: true,
- isErrorWhileCollectingAwsMachines: false,
- awsMachineCollectionErrorMsg: '',
- showModal: false,
- errorDetails: ''
- };
-
- this.closeModal = this.closeModal.bind(this);
- }
-
- componentDidMount() {
- this.authFetch('/api')
- .then(res => res.json())
- .then(res => this.setState({
- ips: res['ip_addresses'],
- selectedIp: res['ip_addresses'][0]
- }));
-
- this.authFetch('/api/local-monkey')
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({runningOnIslandState: 'running'});
- } else {
- this.setState({runningOnIslandState: 'not_running'});
- }
- });
-
- this.fetchAwsInfo();
- this.fetchConfig();
-
- this.authFetch('/api/client-monkey')
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({runningOnClientState: 'running'});
- } else {
- this.setState({runningOnClientState: 'not_running'});
- }
- });
-
- this.props.onStatusChange();
- }
-
- fetchAwsInfo() {
- return this.authFetch('/api/remote-monkey?action=list_aws')
- .then(res => res.json())
- .then(res => {
- let is_aws = res['is_aws'];
- if (is_aws) {
- // On AWS!
- // Checks if there was an error while collecting the aws machines.
- let is_error_while_collecting_aws_machines = (res['error'] != null);
- if (is_error_while_collecting_aws_machines) {
- // There was an error. Finish loading, and display error message.
- this.setState({
- isOnAws: true,
- isErrorWhileCollectingAwsMachines: true,
- awsMachineCollectionErrorMsg: res['error'],
- isLoadingAws: false
- });
- } else {
- // No error! Finish loading and display machines for user
- this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false});
- }
- } else {
- // Not on AWS. Finish loading and don't display the AWS div.
- this.setState({isOnAws: false, isLoadingAws: false});
- }
- });
- }
-
- static generateLinuxCmd(ip, is32Bit) {
- let bitText = is32Bit ? '32' : '64';
- return `wget --no-check-certificate https://${ip}:5000/api/monkey/download/monkey-linux-${bitText}; chmod +x monkey-linux-${bitText}; ./monkey-linux-${bitText} m0nk3y -s ${ip}:5000`
- }
-
- static generateWindowsCmd(ip, is32Bit) {
- let bitText = is32Bit ? '32' : '64';
- return `powershell [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; (New-Object System.Net.WebClient).DownloadFile('https://${ip}:5000/api/monkey/download/monkey-windows-${bitText}.exe','.\\monkey.exe'); ;Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s ${ip}:5000';`;
- }
-
- runLocalMonkey = () => {
- this.authFetch('/api/local-monkey',
- {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({action: 'run'})
- })
- .then(res => res.json())
- .then(res => {
- if (res['is_running']) {
- this.setState({
- runningOnIslandState: 'installing'
- });
- } else {
- /* If Monkey binaries are missing, change the state accordingly */
- if (res['error_text'].startsWith('Copy file failed')) {
- this.setState({
- showModal: true,
- errorDetails: res['error_text']
- }
- );
- }
- this.setState({
- runningOnIslandState: 'not_running'
- });
- }
-
- this.props.onStatusChange();
- });
- };
-
- generateCmdDiv() {
- let isLinux = (this.state.selectedOs.split('-')[0] === 'linux');
- let is32Bit = (this.state.selectedOs.split('-')[1] === '32');
- let cmdText = '';
- if (isLinux) {
- cmdText = RunMonkeyPageComponent2.generateLinuxCmd(this.state.selectedIp, is32Bit);
- } else {
- cmdText = RunMonkeyPageComponent2.generateWindowsCmd(this.state.selectedIp, is32Bit);
- }
- return (
-
-
-
-
-
-
-
- {cmdText}
-
-
- )
- }
-
- setSelectedOs = (key) => {
- this.setState({
- selectedOs: key
- });
- };
-
- setSelectedIp = (key) => {
- this.setState({
- selectedIp: key
- });
- };
-
- static renderIconByState(state) {
- if (state === 'running') {
- return ( )
- } else if (state === 'installing') {
- return ( )
- } else {
- return '';
- }
- }
-
- toggleManual = () => {
- this.setState({
- showManual: !this.state.showManual
- });
- };
-
- toggleAws = () => {
- this.setState({
- showAws: !this.state.showAws
- });
- };
-
- runOnAws = () => {
- this.setState({
- awsClicked: true
- });
-
- let instances = this.awsTable.state.selection.map(x => this.instanceIdToInstance(x));
-
- this.authFetch('/api/remote-monkey',
- {
- method: 'POST',
- headers: {'Content-Type': 'application/json'},
- body: JSON.stringify({type: 'aws', instances: instances, island_ip: this.state.selectedIp})
- }).then(res => res.json())
- .then(res => {
- let result = res['result'];
-
- // update existing state, not run-over
- let prevRes = this.awsTable.state.result;
- for (let key in result) {
- if (result.hasOwnProperty(key)) {
- prevRes[key] = result[key];
- }
- }
- this.awsTable.setState({
- result: prevRes,
- selection: [],
- selectAll: false
- });
-
- this.setState({
- awsClicked: false
- });
- });
- };
-
- fetchConfig() {
- return this.authFetch('/api/configuration/island')
- .then(res => res.json())
- .then(res => {
- return res.configuration;
- })
- }
-
- instanceIdToInstance = (instance_id) => {
- let instance = this.state.awsMachines.find(
- function (inst) {
- return inst['instance_id'] === instance_id;
- });
- return {'instance_id': instance_id, 'os': instance['os']}
-
- };
-
- renderAwsMachinesDiv() {
- return (
-
-
- {
- this.state.ips.length > 1 ?
-
- {this.state.ips.map(ip => {ip} )}
-
- :
- }
-
-
(this.awsTable = r)}
- />
-
-
- Run on selected machines
- {this.state.awsClicked ?
- : null}
-
-
-
- )
- }
-
- closeModal = () => {
- this.setState({
- showModal: false
- })
- };
-
- render() {
- return (
-
- 1. Run Monkey
-
- Go ahead and run the monkey!
- (Or configure the monkey to fine tune its behavior)
-
-
-
- Run on Monkey Island Server
- {RunMonkeyPageComponent2.renderIconByState(this.state.runningOnIslandState)}
-
-
-
-
- OR
-
-
-
-
- Run on a machine of your choice
-
-
-
-
-
- Choose the operating system where you want to run the monkey:
-
-
-
-
-
-
- Windows (32 bit)
-
-
-
-
- Windows (64 bit)
-
-
-
-
- Linux (32 bit)
-
-
-
-
- Linux (64 bit)
-
-
-
-
-
-
- {this.state.ips.length > 1 ?
-
-
-
-
- Choose the interface to communicate with:
-
-
-
-
-
-
- {this.state.ips.map(ip =>
- {ip} )}
-
-
-
-
- :
- }
-
- Copy the following command to your machine and run it with Administrator or root privileges.
-
- {this.generateCmdDiv()}
-
-
- {
- this.state.isLoadingAws ?
-
- : null
- }
- {
- this.state.isOnAws ?
-
- OR
-
- :
- null
- }
- {
- this.state.isOnAws ?
-
-
- Run on AWS machine of your choice
-
-
- :
- null
- }
-
- {
- this.state.isErrorWhileCollectingAwsMachines ?
-
-
-
- Error while collecting AWS machine data. Error
- message: {this.state.awsMachineCollectionErrorMsg}
- Are you sure you've set the correct role on your Island AWS machine?
- Not sure what this is? Read
- the documentation !
-
-
- :
- this.renderAwsMachinesDiv()
- }
-
-
-
-
- Go ahead and monitor the ongoing infection in the Infection Map view.
-
-
- );
- }
-}
-
-export default RunMonkeyPageComponent2;
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js
new file mode 100644
index 000000000..23de0cd21
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOnIslandButton.js
@@ -0,0 +1,113 @@
+import React from 'react';
+import {Button, Col, Row} from 'react-bootstrap';
+
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
+import {faSync} from '@fortawesome/free-solid-svg-icons/faSync';
+import AuthComponent from '../../AuthComponent';
+
+import MissingBinariesModal from '../../ui-components/MissingBinariesModal';
+import '../../../styles/components/RunOnIslandButton.scss';
+
+
+class RunOnIslandButton extends AuthComponent {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ runningOnIslandState: 'not_running',
+ showModal: false,
+ errorDetails: ''
+ };
+
+ this.closeModal = this.closeModal.bind(this);
+ }
+
+ componentDidMount() {
+ this.authFetch('/api/local-monkey')
+ .then(res => res.json())
+ .then(res => {
+ if (res['is_running']) {
+ this.setState({runningOnIslandState: 'running'});
+ } else {
+ this.setState({runningOnIslandState: 'not_running'});
+ }
+ });
+ }
+
+ runIslandMonkey = () => {
+ this.setState({runningOnIslandState: 'installing'}, this.sendRunMonkeyRequest)
+
+ };
+
+ sendRunMonkeyRequest = () => {
+ this.authFetch('/api/local-monkey',
+ {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({action: 'run'})
+ })
+ .then(res => res.json())
+ .then(res => {
+ if (res['is_running']) {
+ this.setState({
+ runningOnIslandState: 'running'
+ });
+ } else {
+ /* If Monkey binaries are missing, change the state accordingly */
+ if (res['error_text'].startsWith('Copy file failed')) {
+ this.setState({
+ showModal: true,
+ errorDetails: res['error_text']
+ }
+ );
+ }
+ this.setState({
+ runningOnIslandState: 'not_running'
+ });
+ }
+ });
+ }
+
+ closeModal = () => {
+ this.setState({
+ showModal: false
+ })
+ };
+
+ getMonkeyRunStateIcon = () => {
+ if (this.state.runningOnIslandState === 'running') {
+ return ( )
+ } else if (this.state.runningOnIslandState === 'installing') {
+ return ( )
+ } else {
+ return '';
+ }
+ }
+
+ render() {
+ let description = this.props.description !== undefined ? ({this.props.description}
) : ''
+ let icon = this.props.icon !== undefined ? () : ''
+ return (
+
+
+
+
+ {icon}
+ {this.props.title}
+ {description}
+ {this.getMonkeyRunStateIcon()}
+
+
+
+ );
+ }
+}
+
+export default RunOnIslandButton;
diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/ManualRunOptions.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
similarity index 66%
rename from monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/ManualRunOptions.js
rename to monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
index 3dbb97f60..a9fe1f8cf 100644
--- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/ManualRunOptions.js
+++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage/RunOptions.js
@@ -3,13 +3,12 @@ import NextSelectionButton from '../../ui-components/inline-selection/NextSelect
import LocalManualRunOptions from './LocalManualRunOptions';
import AuthComponent from '../../AuthComponent';
import {faLaptopCode} from '@fortawesome/free-solid-svg-icons/faLaptopCode';
-import {faNetworkWired} from '@fortawesome/free-solid-svg-icons/faNetworkWired';
-import {faCogs} from '@fortawesome/free-solid-svg-icons/faCogs';
-import {Container} from 'react-bootstrap';
import InlineSelection from '../../ui-components/inline-selection/InlineSelection';
import {cloneDeep} from 'lodash';
+import {faExpandArrowsAlt} from '@fortawesome/free-solid-svg-icons';
+import RunOnIslandButton from './RunOnIslandButton';
-function ManualRunOptions(props) {
+function RunOptions(props) {
const [currentContent, setCurrentContent] = useState(loadingContents());
const [ips, setIps] = useState([]);
@@ -45,30 +44,23 @@ function ManualRunOptions(props) {
}
function getDefaultContents() {
- const newProps = cloneDeep({...props, onBackButtonClick: props.disableManualOptions});
+ const newProps = cloneDeep({...props});
return InlineSelection(defaultContents, newProps);
}
function defaultContents() {
return (
<>
-
+ {
setComponent(LocalManualRunOptions,
{ips: ips, setComponent: setComponent})
}}/>
- {
- }}/>
- {
- }}/>
>
);
}
@@ -76,4 +68,4 @@ function ManualRunOptions(props) {
return currentContent;
}
-export default ManualRunOptions;
+export default RunOptions;
diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
index c7a3d4169..d428c68ee 100644
--- a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
+++ b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/InlineSelection.js
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import BackButton from './BackButton';
-import ManualRunOptions from '../../pages/RunMonkeyPage/ManualRunOptions';
+import ManualRunOptions from '../../pages/RunMonkeyPage/RunOptions';
import {Col, Row, Container} from 'react-bootstrap';
diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/NextSelectionButton.js b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/NextSelectionButton.js
index 433fd076e..7a7c47087 100644
--- a/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/NextSelectionButton.js
+++ b/monkey/monkey_island/cc/ui/src/components/ui-components/inline-selection/NextSelectionButton.js
@@ -13,7 +13,7 @@ export default function nextSelectionButton(props) {
{icon}
- {props.text}
+ {props.title}
{description}
@@ -23,7 +23,8 @@ export default function nextSelectionButton(props) {
}
nextSelectionButton.propTypes = {
- text: PropTypes.string,
+ title: PropTypes.string,
icon: FontAwesomeIcon,
+ description: PropTypes.string,
onButtonClick: PropTypes.func
}
diff --git a/monkey/monkey_island/cc/ui/src/styles/Main.scss b/monkey/monkey_island/cc/ui/src/styles/Main.scss
index 9ac2d680e..707308031 100644
--- a/monkey/monkey_island/cc/ui/src/styles/Main.scss
+++ b/monkey/monkey_island/cc/ui/src/styles/Main.scss
@@ -15,6 +15,7 @@
@import 'components/inline-selection/NextSelectionButton';
@import 'components/inline-selection/BackButton';
@import 'components/inline-selection/CommandDisplay';
+@import 'components/Icons';
// Define custom elements after bootstrap import
diff --git a/monkey/monkey_island/cc/ui/src/styles/components/Icons.scss b/monkey/monkey_island/cc/ui/src/styles/components/Icons.scss
new file mode 100644
index 000000000..ed75d8a9d
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/styles/components/Icons.scss
@@ -0,0 +1,13 @@
+.spinning-icon {
+ animation: spin-animation 0.5s infinite;
+ display: inline-block;
+}
+
+@keyframes spin-animation {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(359deg);
+ }
+}
diff --git a/monkey/monkey_island/cc/ui/src/styles/components/RunOnIslandButton.scss b/monkey/monkey_island/cc/ui/src/styles/components/RunOnIslandButton.scss
new file mode 100644
index 000000000..e13c0e8a4
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/styles/components/RunOnIslandButton.scss
@@ -0,0 +1,7 @@
+.monkey-on-island-run-state-icon {
+ display: inline-block;
+ position: absolute;
+ right: 23px;
+ top: 28%;
+ font-size: 1.1em;
+}