diff --git a/monkey/monkey_island/cc/server_config.json b/monkey/monkey_island/cc/server_config.json index ecc4c1708..708b5c29c 100644 --- a/monkey/monkey_island/cc/server_config.json +++ b/monkey/monkey_island/cc/server_config.json @@ -1,4 +1,6 @@ { "server_config": "password", - "deployment": "develop" -} + "deployment": "develop", + "user": "test", + "password_hash": "9ece086e9bac491fac5c1d1046ca11d737b92a2b2ebd93f005d7b710110c0a678288166e7fbe796883a4f2e9b3ca9f484f521d0ce464345cc1aec96779149c14" +} \ No newline at end of file diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js new file mode 100644 index 000000000..1d1e43be4 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/PbaInput.js @@ -0,0 +1,91 @@ +import React from 'react'; +import AuthComponent from '../AuthComponent'; + +import {FilePond} from 'react-filepond'; +import 'filepond/dist/filepond.min.css'; + + +class PbaInput extends AuthComponent { + + constructor(props) { + super(props); + console.log("Constructor called"); + // set schema from server + this.state = this.getStateFromProps(this.props); + } + + getStateFromProps(props){ + let options = props.options + // set schema from server + return { + PBAFile: options.PbaFile, + filename: options.filename, + apiEndpoint: options.apiEndpoint, + setPbaFile: options.setPbaFile + }; + } + + getPBAfile() { + if (this.state.PBAFile.length !== 0) { + return this.state.PBAFile + return PbaInput.getMockPBAfile(this.state.PBAFile) + } else if (this.state.filename) { + return PbaInput.getFullPBAfile(this.state.filename) + } + } + + static getMockPBAfile(mockFile) { + let pbaFile = [{ + name: mockFile.name, + source: mockFile.name, + options: { + type: 'limbo' + } + }]; + pbaFile[0].options.file = mockFile; + return pbaFile + } + + + static getFullPBAfile(filename) { + return [{ + source: filename, + options: { + type: 'limbo' + } + }]; + } + + getServerParams(path) { + return { + url: path, + process: this.getRequestParams(), + revert: this.getRequestParams(), + restore: this.getRequestParams(), + load: this.getRequestParams(), + fetch: this.getRequestParams() + } + } + + getRequestParams() { + return {headers: {'Authorization': this.jwtHeader}} + } + + render() { + return ( { + if (fileItems.length > 0) { + this.state.setPbaFile([fileItems[0].file], fileItems[0].file.name) + } else { + this.state.setPbaFile([], "") + } + }} + />) + } +} + + +export default PbaInput; diff --git a/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js new file mode 100644 index 000000000..ba1a6b19e --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/configuration-components/UiSchema.js @@ -0,0 +1,70 @@ +import ArrayFieldTemplate from "../ui-components/AdvancedMultipleSelect"; +import PbaInput from "./PbaInput"; +import {API_PBA_LINUX, API_PBA_WINDOWS} from '../pages/ConfigurePage'; + +export default function UiSchema(props) { + const UiSchema = { + basic: { + 'ui:order': ['general', 'credentials'], + credentials: { + exploit_password_list: { + "ui:ArrayFieldTemplate": ArrayFieldTemplate + } + } + }, + basic_network: {}, + monkey: { + behaviour: { + custom_PBA_linux_cmd: { + 'ui:widget': 'textarea', + 'ui:emptyValue': '' + }, + PBA_linux_file: { + 'ui:widget': PbaInput, + 'ui:options': { + PbaFile: props.configuration.PBAlinuxFile, + filename: props.configuration.configuration.monkey.behaviour.PBA_linux_filename, + apiEndpoint: API_PBA_LINUX, + setPbaFile: props.setPbaFileLinux + } + }, + custom_PBA_windows_cmd: { + 'ui:widget': 'textarea', + 'ui:emptyValue': '' + }, + PBA_windows_file: { + 'ui:widget': PbaInput, + 'ui:options': { + PbaFile: props.configuration.PBAwindowsFile, + filename: props.configuration.configuration.monkey.behaviour.PBA_windows_filename, + apiEndpoint: API_PBA_WINDOWS, + setPbaFile: props.setPbaFileWindows + } + }, + PBA_linux_filename: { + classNames: 'linux-pba-file-info', + 'ui:emptyValue': '' + }, + PBA_windows_filename: { + classNames: 'windows-pba-file-info', + 'ui:emptyValue': '' + } + } + }, + cnc: {}, + network: {}, + exploits: { + general: { + exploiter_classes: { + "ui:ArrayFieldTemplate": ArrayFieldTemplate + } + } + }, + internal: { + general: { + started_on_island: {'ui:widget': 'hidden'} + } + } + } + return UiSchema[props.configuration.selectedSection] +} diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index 2472f5adc..ca07d3604 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -3,9 +3,8 @@ import Form from 'react-jsonschema-form-bs4'; import {Col, Modal, Nav, Button} from 'react-bootstrap'; import FileSaver from 'file-saver'; import AuthComponent from '../AuthComponent'; -import {FilePond} from 'react-filepond'; -import 'filepond/dist/filepond.min.css'; import ConfigMatrixComponent from '../attack/ConfigMatrixComponent'; +import UiSchema from '../configuration-components/UiSchema'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {faInfoCircle} from '@fortawesome/free-solid-svg-icons/faInfoCircle'; import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck'; @@ -13,20 +12,19 @@ import {faExclamationCircle} from '@fortawesome/free-solid-svg-icons/faExclamati const ATTACK_URL = '/api/attack'; const CONFIG_URL = '/api/configuration/island'; +export const API_PBA_LINUX = '/api/fileUpload/PBAlinux'; +export const API_PBA_WINDOWS = '/api/fileUpload/PBAwindows'; class ConfigurePageComponent extends AuthComponent { constructor(props) { super(props); - this.PBAwindowsPond = null; - this.PBAlinuxPond = null; this.currentSection = 'attack'; this.currentFormData = {}; this.initialConfig = {}; this.initialAttackConfig = {}; this.sectionsOrder = ['attack', 'basic', 'basic_network', 'monkey', 'cnc', 'network', 'exploits', 'internal']; - this.uiSchemas = this.getUiSchemas(); - // set schema from server + this.state = { schema: {}, configuration: {}, @@ -34,51 +32,11 @@ class ConfigurePageComponent extends AuthComponent { lastAction: 'none', sections: [], selectedSection: 'attack', - PBAwinFile: [], - PBAlinuxFile: [], - showAttackAlert: false - }; - } + showAttackAlert: false, - getUiSchemas() { - return ({ - basic: {'ui:order': ['general', 'credentials']}, - basic_network: {}, - monkey: { - behaviour: { - custom_PBA_linux_cmd: { - 'ui:widget': 'textarea', - 'ui:emptyValue': '' - }, - PBA_linux_file: { - 'ui:widget': this.PBAlinux - }, - custom_PBA_windows_cmd: { - 'ui:widget': 'textarea', - 'ui:emptyValue': '' - }, - PBA_windows_file: { - 'ui:widget': this.PBAwindows - }, - PBA_linux_filename: { - classNames: 'linux-pba-file-info', - 'ui:emptyValue': '' - }, - PBA_windows_filename: { - classNames: 'windows-pba-file-info', - 'ui:emptyValue': '' - } - } - }, - cnc: {}, - network: {}, - exploits: {}, - internal: { - general: { - started_on_island: {'ui:widget': 'hidden'} - } - } - }) + PBAwindowsFile: [], + PBAlinuxFile: [] + }; } setInitialConfig(config) { @@ -122,7 +80,7 @@ class ConfigurePageComponent extends AuthComponent { .then(res => res.json()) .then(data => { this.setInitialConfig(data.configuration); - this.setState({configuration: data.configuration}) + this.setState({configuration: data.configuration}); }) }; @@ -271,7 +229,6 @@ class ConfigurePageComponent extends AuthComponent { }; resetConfig = () => { - this.removePBAfiles(); this.authFetch(CONFIG_URL, { method: 'POST', @@ -280,14 +237,15 @@ class ConfigurePageComponent extends AuthComponent { }) .then(res => res.json()) .then(res => { - this.setState({ - lastAction: 'reset', - schema: res.schema, - configuration: res.configuration - }); - this.setInitialConfig(res.configuration); - this.props.onStatusChange(); - }); + this.setState({ + lastAction: 'reset', + schema: res.schema, + configuration: res.configuration + }); + this.setInitialConfig(res.configuration); + this.props.onStatusChange(); + } + ); this.authFetch(ATTACK_URL, { method: 'POST', headers: {'Content-Type': 'application/json'}, @@ -298,23 +256,22 @@ class ConfigurePageComponent extends AuthComponent { this.setState({attackConfig: res.configuration}); this.setInitialAttackConfig(res.configuration); }) + + this.removePBAfile(API_PBA_WINDOWS, this.setPbaFileWindows) + this.removePBAfile(API_PBA_LINUX, this.setPbaFileLinux) }; - removePBAfiles() { - // We need to clean files from widget, local state and configuration (to sync with bac end) - if (this.PBAwindowsPond !== null) { - this.PBAwindowsPond.removeFile(); - } - if (this.PBAlinuxPond !== null) { - this.PBAlinuxPond.removeFile(); - } + removePBAfile(apiEndpoint, setParamsFnc) { + this.sendPbaRemoveRequest(apiEndpoint) + setParamsFnc([], "") + } + + sendPbaRemoveRequest(apiEndpoint) { let request_options = { method: 'DELETE', headers: {'Content-Type': 'text/plain'} }; - this.authFetch('/api/fileUpload/PBAlinux', request_options); - this.authFetch('/api/fileUpload/PBAwindows', request_options); - this.setState({PBAlinuxFile: [], PBAwinFile: []}); + this.authFetch(apiEndpoint, request_options); } setConfigOnImport = (event) => { @@ -366,82 +323,6 @@ class ConfigurePageComponent extends AuthComponent { event.target.value = null; }; - PBAwindows = () => { - return ( { - this.setState({ - PBAwinFile: fileItems.map(fileItem => fileItem.file) - }) - }} - ref={ref => this.PBAwindowsPond = ref} - />) - }; - - PBAlinux = () => { - return ( { - this.setState({ - PBAlinuxFile: fileItems.map(fileItem => fileItem.file) - }) - }} - ref={ref => this.PBAlinuxPond = ref} - />) - }; - - getWinPBAfile() { - if (this.state.PBAwinFile.length !== 0) { - return ConfigurePageComponent.getMockPBAfile(this.state.PBAwinFile[0]) - } else if (this.state.configuration.monkey.behaviour.PBA_windows_filename) { - return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_windows_filename) - } - } - - getLinuxPBAfile() { - if (this.state.PBAlinuxFile.length !== 0) { - return ConfigurePageComponent.getMockPBAfile(this.state.PBAlinuxFile[0]) - } else if (this.state.configuration.monkey.behaviour.PBA_linux_filename) { - return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_linux_filename) - } - } - - static getFullPBAfile(filename) { - return [{ - source: filename, - options: { - type: 'limbo' - } - }]; - } - - static getMockPBAfile(mockFile) { - let pbaFile = [{ - source: mockFile.name, - options: { - type: 'limbo' - } - }]; - pbaFile[0].options.file = mockFile; - return pbaFile - } - renderMatrix = () => { return () }; - renderConfigContent = (displayedSchema) => { return (
{this.renderBasicNetworkWarning()}
) }; + setPbaFileWindows = (pbaFile, filename) => { + let pbaFileDeepCopy = JSON.parse(JSON.stringify(pbaFile)) + let config = this.state.configuration + config.monkey.behaviour.PBA_windows_filename = filename + this.setState({ + PBAwindowsFile: pbaFileDeepCopy, + configuration: config + }) + } + + setPbaFileLinux = (pbaFile, filename) => { + let pbaFileDeepCopy = JSON.parse(JSON.stringify(pbaFile)) + let config = this.state.configuration + config.monkey.behaviour.PBA_linux_filename = filename + console.log(config); + this.setState({ + PBAlinuxFile: pbaFileDeepCopy, + configuration: config + }) + } + renderBasicNetworkWarning = () => { if (this.state.selectedSection === 'basic_network') { return (
@@ -495,6 +400,7 @@ class ConfigurePageComponent extends AuthComponent { displayedSchema = this.state.schema['properties'][this.state.selectedSection]; displayedSchema['definitions'] = this.state.schema['definitions']; } + let content = ''; if (this.state.selectedSection === 'attack' && Object.entries(this.state.attackConfig).length !== 0) { content = this.renderMatrix() diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/AdvancedMultipleSelect.js b/monkey/monkey_island/cc/ui/src/components/ui-components/AdvancedMultipleSelect.js new file mode 100644 index 000000000..70e0953a0 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/AdvancedMultipleSelect.js @@ -0,0 +1,50 @@ +import React from "react"; +import Button from 'react-bootstrap/Button'; + + +function ArrayFieldTemplate(props) { + return ( +
+ {props.items && + props.items.map(element => ( +
+
{element.children}
+ {element.hasMoveDown && ( + + )} + {element.hasMoveUp && ( + + )} + +
+
+ ))} + + {props.canAdd && ( +
+

+ +

+
+ )} +
+ ); +} + +export default ArrayFieldTemplate;