From 4275a9df2ad9b4f16338c5a5b21c3e4c599a42f0 Mon Sep 17 00:00:00 2001 From: msm-code Date: Wed, 29 Apr 2020 22:02:23 +0200 Subject: [PATCH] Reuse storage UI for config (#132) --- src/app.py | 25 +++++ src/mqueryfront/src/App.js | 2 + src/mqueryfront/src/ConfigEntries.js | 162 +++++++++++++++++++++++++++ src/mqueryfront/src/ConfigPage.js | 41 +++++++ src/mqueryfront/src/Navigation.js | 7 ++ src/schema.py | 13 +++ 6 files changed, 250 insertions(+) create mode 100644 src/mqueryfront/src/ConfigEntries.js create mode 100644 src/mqueryfront/src/ConfigPage.js diff --git a/src/app.py b/src/app.py index 47b58646..1f20616a 100644 --- a/src/app.py +++ b/src/app.py @@ -19,12 +19,14 @@ from schema import ( JobsSchema, JobSchema, + RequestConfigEdit, RequestQueryMethod, QueryRequestSchema, QueryResponseSchema, ParseResponseSchema, MatchesSchema, StatusSchema, + ConfigSchema, UserSettingsSchema, UserInfoSchema, UserAuthSchema, @@ -118,6 +120,29 @@ def job_cancel(job_id: str) -> StatusSchema: return StatusSchema(status="ok") +@app.get("/api/config", response_model=List[ConfigSchema]) +def config_list() -> List[ConfigSchema]: + return [ + ConfigSchema( + plugin="default", + key="mwdb_url", + value="http://mwdb.cert.pl", + description="URL of a a mwdb service.", + ), + ConfigSchema( + plugin="default", + key="stuff", + value="1337", + description="Important configuration key.", + ), + ] + + +@app.post("/api/config/edit", response_model=StatusSchema) +def config_edit(data: RequestConfigEdit = Body(...)) -> StatusSchema: + return StatusSchema(status="ok") + + @app.get("/api/user/settings", response_model=UserSettingsSchema) def user_settings() -> UserSettingsSchema: return UserSettingsSchema(can_register=True, plugin_name="Redis") diff --git a/src/mqueryfront/src/App.js b/src/mqueryfront/src/App.js index c67b53b0..477425f1 100644 --- a/src/mqueryfront/src/App.js +++ b/src/mqueryfront/src/App.js @@ -4,6 +4,7 @@ import Navigation from "./Navigation"; import QueryPage from "./QueryPage"; import RecentPage from "./RecentPage"; import StatusPage from "./StatusPage"; +import ConfigPage from "./ConfigPage"; import "./App.css"; class App extends Component { @@ -16,6 +17,7 @@ class App extends Component { + diff --git a/src/mqueryfront/src/ConfigEntries.js b/src/mqueryfront/src/ConfigEntries.js new file mode 100644 index 00000000..fb4dd6df --- /dev/null +++ b/src/mqueryfront/src/ConfigEntries.js @@ -0,0 +1,162 @@ +import React, { Component } from "react"; +import ErrorBoundary from "./ErrorBoundary"; +import axios from "axios"; +import { API_URL } from "./config"; + +class ConfigRow extends Component { + constructor(props) { + super(props); + + this.state = { + edit: false, + value: this.props.value, + }; + + this.save = this.save.bind(this); + this.cancel = this.cancel.bind(this); + this.edit = this.edit.bind(this); + this.handleEdit = this.handleEdit.bind(this); + } + + save() { + this.setState({ + edit: false, + }); + axios.post(API_URL + "/config/edit", { + plugin: this.props.plugin, + key: this.props.keyName, + value: this.props.value, + }); + } + + cancel() { + this.setState({ + edit: false, + value: this.props.value, + }); + } + + edit() { + this.setState({ + edit: true, + }); + } + + handleEdit(event) { + this.setState({ + value: event.value, + }); + } + + render() { + let valueControl; + let editToggle; + if (this.state.edit) { + valueControl = ( + + ); + editToggle = ( +
+ + +
+ ); + } else { + valueControl = {this.props.value}; + editToggle = ( + + ); + } + + return ( + + + + {this.props.plugin} + + + + + {this.props.keyName} + + + +
+
{valueControl}
+
{editToggle}
+
+ + + {this.props.description} + + + ); + } +} + +class ConfigEntryList extends Component { + render() { + const configRows = this.props.config.map((config) => ( + + )); + + return ( + +
+ + + + + + + + + + {configRows} +
pluginkeyvaluedescription
+
+
+ ); + } +} + +export default ConfigEntryList; diff --git a/src/mqueryfront/src/ConfigPage.js b/src/mqueryfront/src/ConfigPage.js new file mode 100644 index 00000000..209566ee --- /dev/null +++ b/src/mqueryfront/src/ConfigPage.js @@ -0,0 +1,41 @@ +import React, { Component } from "react"; +import ErrorBoundary from "./ErrorBoundary"; +import ConfigEntryList from "./ConfigEntries"; +import axios from "axios"; +import { API_URL } from "./config"; +import { Link } from "react-router-dom"; + +class ConfigPage extends Component { + constructor(props) { + super(props); + + this.state = { + config: [], + error: null, + }; + } + + componentDidMount() { + axios + .get(API_URL + "/config") + .then((response) => { + this.setState({ config: response.data }); + }) + .catch((error) => { + this.setState({ error: error }); + }); + } + + render() { + return ( + +
+

Config

+ +
+
+ ); + } +} + +export default ConfigPage; diff --git a/src/mqueryfront/src/Navigation.js b/src/mqueryfront/src/Navigation.js index eee67d3e..2c0c59cc 100644 --- a/src/mqueryfront/src/Navigation.js +++ b/src/mqueryfront/src/Navigation.js @@ -42,6 +42,13 @@ class Navigation extends Component { Recent jobs + {process.env.NODE_ENV == "development" ? ( +
  • + + Config + +
  • + ) : undefined}
  • Status diff --git a/src/schema.py b/src/schema.py index 9ee9bb7b..186568d9 100644 --- a/src/schema.py +++ b/src/schema.py @@ -23,6 +23,13 @@ class JobsSchema(BaseModel): jobs: List[JobSchema] +class ConfigSchema(BaseModel): + plugin: str + key: str + value: str + description: str + + class TaskSchema(BaseModel): connection_id: str epoch_ms: int @@ -37,6 +44,12 @@ class RequestQueryMethod(str, Enum): parse = "parse" +class RequestConfigEdit(BaseModel): + plugin: str + key: str + value: str + + class QueryRequestSchema(BaseModel): raw_yara: str taint: Optional[str]