Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2698 plugin selector UI schema #2822

Merged
merged 3 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {ObjectFieldTemplateProps} from '@rjsf/utils';
import React, {useState} from 'react';
import {Dropdown} from 'react-bootstrap';

const PluginSelector = (props) => {
let selectOptions = [];
for (const [name, plugin] of Object.entries(props.plugins)) {
selectOptions.push(
<Dropdown.Item onClick={() => props.onClick(name)}
eventKey={`plugin['title']`}>
{plugin['title']}</Dropdown.Item>
)}

return (
<Dropdown>
<Dropdown.Toggle variant="success" id="dropdown-basic">
Select a plugin
</Dropdown.Toggle>
<Dropdown.Menu>
{selectOptions}
</Dropdown.Menu>
</Dropdown>
)
}


export default function PluginSelectorTemplate(props: ObjectFieldTemplateProps) {

function getPluginDisplay(plugin, allPlugins){
let selectedPlugin = allPlugins.filter((pluginInArray) => pluginInArray.name == plugin)
if(selectedPlugin.length === 1){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably impossible for this to be a length other than 0 or 1, but we can code a bit more defensively:

Suggested change
if(selectedPlugin.length === 1){
if(selectedPlugin.length > 0){

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how much of this code will remain, let's re-consider this once the plugin selector is more fleshed-out

return <div className="property-wrapper">{selectedPlugin[0].content}</div>
}
}

let [element, setElement] = useState(null);
return (
<div>
{props.title}
{props.description}
<PluginSelector plugins={{"TODO": {"title": "pass in actual schema"}}}
onClick={(pluginName) => {
setElement(pluginName)
}}/>
{getPluginDisplay(element, props.properties)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import InfoBox from './InfoBox';
import TextBox from './TextBox.js';
import WarningBox from './WarningBox';
import SensitiveTextInput from '../ui-components/SensitiveTextInput';
import PluginSelectorTemplate from './PluginSelectorTemplate';

export default function UiSchema(props) {
const UiSchema = {
propagation: {
exploitation: {
exploiters: {
classNames: 'config-template-no-header',
'ui:widget': AdvancedMultiSelect
'ui:ObjectFieldTemplate': PluginSelectorTemplate
},
options: {
http_ports: {
Expand Down
19 changes: 17 additions & 2 deletions monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import Form from '@rjsf/bootstrap-4';
import {Col, Nav} from 'react-bootstrap';
import _ from 'lodash';
import AuthComponent from '../AuthComponent';
import UiSchema from '../configuration-components/UiSchema';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
Expand All @@ -26,6 +27,7 @@ import {
import validator from '@rjsf/validator-ajv8';

const CONFIG_URL = '/api/agent-configuration';
const SCHEMA_URL = '/api/agent-configuration-schema';
const RESET_URL = '/api/reset-agent-configuration';
const CONFIGURED_PROPAGATION_CREDENTIALS_URL = '/api/propagation-credentials/configured-credentials';

Expand All @@ -46,7 +48,7 @@ class ConfigurePageComponent extends AuthComponent {
currentFormData: {},
importCandidateConfig: null,
lastAction: 'none',
schema: {},
schema: SCHEMA,
sections: [],
selectedSection: this.currentSection,
showUnsafeOptionsConfirmation: false,
Expand Down Expand Up @@ -77,7 +79,21 @@ class ConfigurePageComponent extends AuthComponent {
this.initialConfig = JSON.parse(JSON.stringify(config));
}

injectExploitersIntoLegacySchema = (newSchema) => {
// legacy schema is defined in UI,
// but we should use the schema provided by "/api/agent-configuration-schema"
// Remove when #2750 is done
let injectedSchema = _.cloneDeep(this.state.schema);
injectedSchema['properties']['propagation']['properties']['exploitation']['properties']['exploiters'] =
newSchema['definitions']['ExploitationConfiguration']['properties']['exploiters']
return injectedSchema;
}

componentDidMount = () => {
this.authFetch(SCHEMA_URL).then(res => res.json())
.then(schema => {
this.setState({schema: this.injectExploitersIntoLegacySchema(schema)})
})
this.authFetch(CONFIG_URL).then(res => res.json())
.then(monkeyConfig => {
let sections = [];
Expand All @@ -92,7 +108,6 @@ class ConfigurePageComponent extends AuthComponent {
}

this.setState({
schema: SCHEMA,
configuration: monkeyConfig,
sections: sections,
currentFormData: monkeyConfig[this.state.selectedSection]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import EXPLOITATION_OPTIONS_CONFIGURATION_SCHEMA from '../exploitationOptions.js';
import {EXPLOITERS} from '../exploiterClasses';


const EXPLOITATION_CONFIGURATION_SCHEMA = {
'title': 'Propagation',
'properties': {
'exploiters': {
'items': EXPLOITERS,
'title': 'Exploiters',
'type': 'array',
'uniqueItems': true
},
// Exploiters get injected from schema generated on the back end
},

'options': EXPLOITATION_OPTIONS_CONFIGURATION_SCHEMA
},
'type': 'object'
Expand Down