Refactoring file upload UI -> server to be via filepond

This commit is contained in:
VakarisZ 2019-02-25 14:29:47 +02:00
parent 4292fc845b
commit a51d7da1f9
8 changed files with 471 additions and 465 deletions

View File

@ -29,6 +29,7 @@ from cc.resources.telemetry import Telemetry
from cc.resources.telemetry_feed import TelemetryFeed
from cc.resources.pba_file_download import PBAFileDownload
from cc.services.config import ConfigService
from cc.resources.file_upload import FileUpload
__author__ = 'Barak'
@ -118,6 +119,7 @@ def init_app(mongo_url):
api.add_resource(Log, '/api/log', '/api/log/')
api.add_resource(IslandLog, '/api/log/island/download', '/api/log/island/download/')
api.add_resource(PBAFileDownload, '/api/pba/download/<string:path>')
api.add_resource(FileUpload, '/api/fileUpload/<string:file_type>')
api.add_resource(RemoteRun, '/api/remote-monkey', '/api/remote-monkey/')
return app

View File

@ -0,0 +1,29 @@
import flask_restful
from flask import request, send_from_directory
from cc.services.config import ConfigService
import os
from werkzeug.utils import secure_filename
UPLOADS_DIR = "./monkey_island/cc/userUploads"
class FileUpload(flask_restful.Resource):
# Used by monkey. can't secure.
def get(self, path):
return send_from_directory(UPLOADS_DIR, path)
def post(self, file_type):
if file_type == 'PBAlinux':
config = ConfigService.get_config()
file_info = ConfigService.get_config_value(['monkey', 'behaviour', 'custom_post_breach', 'linux_file_info'])
filename = secure_filename(request.files['filepond'].filename)
file_path = os.path.join(UPLOADS_DIR, filename)
request.files['filepond'].save(file_path)
file_size = os.path.getsize(file_path)
# config['monkey']['behaviour']['cutom_post_breach']['linux_file_info']['size'] = file_size
# config['monkey']['behaviour']['cutom_post_breach']['linux_file_info']['name'] = filename
# ConfigService.update_config(config, True)
elif file_type == 'PBAwindows':
request.files['filepond'].save("./useless.file")

View File

@ -70,11 +70,13 @@ class ConfigService:
:param is_initial_config: If True, returns the value of the initial config instead of the current config.
:param should_decrypt: If True, the value of the config key will be decrypted
(if it's in the list of encrypted config values).
:return: The value of the requested config key.
:return: The value of the requested config key or False, if such key doesn't exist.
"""
config_key = functools.reduce(lambda x, y: x + '.' + y, config_key_as_arr)
config = mongo.db.config.find_one({'name': 'initial' if is_initial_config else 'newconfig'}, {config_key: 1})
for config_key_part in config_key_as_arr:
if config_key_part not in config:
return False
config = config[config_key_part]
if should_decrypt:
if config_key_as_arr in ENCRYPTED_CONFIG_ARRAYS:
@ -142,8 +144,6 @@ class ConfigService:
@staticmethod
def update_config(config_json, should_encrypt):
if 'custom_post_breach' in config_json['monkey']['behaviour']:
ConfigService.add_PBA_files(config_json['monkey']['behaviour']['custom_post_breach'])
if should_encrypt:
try:
ConfigService.encrypt_config(config_json)
@ -211,13 +211,7 @@ class ConfigService:
if instance != {}:
return
for property, subschema in properties.iteritems():
main_dict = {}
for property2, subschema2 in subschema["properties"].iteritems():
sub_dict = {}
for property3, subschema3 in subschema2["properties"].iteritems():
if "default" in subschema3:
sub_dict[property3] = subschema3["default"]
main_dict[property2] = sub_dict
main_dict = ConfigService.r_get_properties(subschema)
instance.setdefault(property, main_dict)
for error in validate_properties(validator, properties, instance, schema):
@ -227,6 +221,16 @@ class ConfigService:
validator_class, {"properties": set_defaults},
)
@staticmethod
def r_get_properties(schema):
if "default" in schema:
return schema["default"]
if "properties" in schema:
dict_ = {}
for property, subschema in schema["properties"].iteritems():
dict_[property] = ConfigService.r_get_properties(subschema)
return dict_
@staticmethod
def decrypt_config(config):
ConfigService._encrypt_or_decrypt_config(config, True)
@ -312,13 +316,15 @@ class ConfigService:
@staticmethod
def remove_PBA_files():
# Remove PBA files
current_config = ConfigService.get_config()
if current_config:
linux_file_name = ConfigService.get_config_value(['monkey', 'behaviour', 'custom_post_breach', 'linux_file_info', 'name'])
windows_file_name = ConfigService.get_config_value(['monkey', 'behaviour', 'custom_post_breach', 'windows_file_info', 'name'])
ConfigService.remove_file(linux_file_name)
ConfigService.remove_file(windows_file_name)
if ConfigService.get_config():
linux_file_name = ConfigService.get_config_value(
['monkey', 'behaviour', 'custom_post_breach', 'linux_file_info', 'name'])
windows_file_name = ConfigService.get_config_value(
['monkey', 'behaviour', 'custom_post_breach', 'windows_file_info', 'name'])
if linux_file_name:
ConfigService.remove_file(linux_file_name)
if windows_file_name:
ConfigService.remove_file(windows_file_name)
@staticmethod
def remove_file(file_name):

View File

@ -375,8 +375,6 @@ SCHEMA = {
}
}
},
"default": [
],
"description": "List of actions the Monkey will run post breach"
},
"self_delete_in_cleanup": {

File diff suppressed because it is too large Load Diff

View File

@ -68,6 +68,7 @@
"core-js": "^2.5.7",
"downloadjs": "^1.4.7",
"fetch": "^1.1.0",
"filepond": "^4.2.0",
"js-file-download": "^0.4.4",
"json-loader": "^0.5.7",
"jwt-decode": "^2.2.0",
@ -83,6 +84,7 @@
"react-dimensions": "^1.3.0",
"react-dom": "^16.5.2",
"react-fa": "^5.0.0",
"react-filepond": "^7.0.1",
"react-graph-vis": "^1.0.2",
"react-json-tree": "^0.11.0",
"react-jsonschema-form": "^1.0.5",

View File

@ -3,6 +3,8 @@ import Form from 'react-jsonschema-form';
import {Col, Nav, NavItem} from 'react-bootstrap';
import fileDownload from 'js-file-download';
import AuthComponent from '../AuthComponent';
import { FilePond, registerPlugin } from 'react-filepond';
import 'filepond/dist/filepond.min.css';
class ConfigurePageComponent extends AuthComponent {
constructor(props) {
@ -59,16 +61,6 @@ class ConfigurePageComponent extends AuthComponent {
})
.then(res => res.json())
.then(res => {
// Leave PBA files on external configuration
if ('linux_file' in this.state.configuration.monkey.behaviour.custom_post_breach){
let linux_file = this.state.configuration.monkey.behaviour.custom_post_breach.linux_file;
res.configuration.monkey.behaviour.custom_post_breach.windows_file = linux_file;
}
if ('windows_file' in this.state.configuration.monkey.behaviour.custom_post_breach){
let windows_file = this.state.configuration.monkey.behaviour.custom_post_breach.windows_file;
res.configuration.monkey.behaviour.custom_post_breach.linux_file = windows_file;
}
this.setState({
lastAction: 'saved',
schema: res.schema,
@ -160,6 +152,15 @@ class ConfigurePageComponent extends AuthComponent {
});
};
PBAwindows = () => {
return (<FilePond server='/api/fileUpload/PBAwindows'/>)
};
PBAlinux = () => {
return (<FilePond server='/api/fileUpload/PBAlinux'/>)
};
render() {
let displayedSchema = {};
const uiSchema = {
@ -168,8 +169,14 @@ class ConfigurePageComponent extends AuthComponent {
linux: {
"ui:widget": "textarea"
},
linux_file: {
"ui:widget": this.PBAlinux
},
windows: {
"ui:widget": "textarea"
},
windows_file: {
"ui:widget": this.PBAwindows
}
}
}

View File

@ -4,6 +4,8 @@ import ReactDOM from 'react-dom';
import 'babel-polyfill';
import App from './components/Main';
import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars
import { FilePond, registerPlugin } from 'react-filepond';
import 'filepond/dist/filepond.min.css';
// Render the main component into the dom
ReactDOM.render(<App />, document.getElementById('app'));