Refactoring file upload UI -> server to be via filepond
This commit is contained in:
parent
4292fc845b
commit
a51d7da1f9
|
@ -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
|
||||
|
|
|
@ -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")
|
|
@ -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):
|
||||
|
|
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'));
|
||||
|
|
Loading…
Reference in New Issue