import React from 'react'; import { EuiForm, EuiFormRow, EuiTitle, EuiSpacer, EuiFieldText, EuiFieldNumber, EuiSelect, EuiFlexGroup, EuiFlexItem, EuiButton, EuiButtonIcon, } from '@elastic/eui'; // Default data types in The Hive const DEFAULT_THE_HIVE_TYPES = [ '', 'autonomous-system', 'domain', 'file', 'filename', 'fqdn', 'hash', 'ip', 'mail', 'mail_subject', 'regexp', 'registry', 'uri_path', 'url', 'user-agent', 'other', ]; // Options for EuiSelect for selection of field's data type in TheHive const typesOptions = DEFAULT_THE_HIVE_TYPES.map( dt => ({value: dt, text: dt}) ); export function optionsEditor(props) { //console.log("editor render(), props:", props); const { stateParams, setValue, setValidity, vis } = props; // onClick/onChange handlers const obsAddNew = () => { const newObsFields = [...stateParams.obsFields, {name: "", type: "", cnt: 100}]; // For some reason, first click on the button after editor is loaded does // nothing. Calling setValue twice here fixes it. setValue("obsFields", newObsFields); setValue("obsFields", newObsFields); // setValidity(false); // since new row is empty, form is always invalid }; const obsRemove = (ix) => { let newArray = [...stateParams.obsFields]; newArray.splice(ix, 1); setValue("obsFields", newArray); // validate(); } const obsSetName = (ix, name) => { let newArray = [...stateParams.obsFields]; newArray[ix].name = name; setValue("obsFields", newArray); // validate(); } const obsSetType = (ix, type) => { let newArray = [...stateParams.obsFields]; newArray[ix].type = type; setValue("obsFields", newArray); // validate(); } const obsSetCnt = (ix, cnt) => { let newArray = [...stateParams.obsFields]; newArray[ix].cnt = parseInt(cnt); setValue("obsFields", newArray); // validate(); } // const validate = () => { // let valid = true; // for (let field of stateParams.obsFields) { // if (field.name == "" || field.type == "" || field.cnt == "") { // valid = false; // break; // } // } // // TODO check for duplicate fields // setValidity(valid); // } // Get list of all fields in index (except those beginning with "_" or "@") // and create "options" parameter for EuiSelect. // Also, fields with "aggregatable=false" are removed, as they can't be used // with "terms" aggregation we need. // See this for details: https://www.elastic.co/guide/en/elasticsearch/reference/7.x/fielddata.html // Empty field is added at the beginning, meaning "no selection yet". const fieldOptions = [{value: "", text: ""}].concat( vis.indexPattern.fields.raw.filter( f => (f.name[0] != "_" && f.name[0] != "@" && f.aggregatable) ).map( f => ({value: f.name, text: `${f.name} (${f.type})`}) ) ); return <EuiForm> <EuiFormRow fullWidth={true} label="Base URL of The Hive"> <EuiFieldText fullWidth={true} value={stateParams.url} onChange={e => setValue('url', e.target.value)} isInvalid={stateParams.url == ""} /> </EuiFormRow> <EuiFlexGroup> <EuiFlexItem grow={1}> <EuiFormRow label="API key to access The Hive" helpText="API key of a user with permission to create cases (not admin)."> <EuiFieldText fullWidth={true} value={stateParams.apikey} onChange={e => setValue('apikey', e.target.value)} isInvalid={stateParams.apikey == ""} /> </EuiFormRow> </EuiFlexItem> <EuiFlexItem grow={1}> <EuiFormRow label="Assignee" helpText="Assign created cases to this user instead of the owner of the API key (optional, if set, it must be a valid username from The Hive instance). **Note: This currently doesn't work due to a bug in The Hive.**"> <EuiFieldText value={stateParams.owner} onChange={e => setValue('owner', e.target.value)} /> </EuiFormRow> </EuiFlexItem> </EuiFlexGroup> <EuiTitle size="s"><h3>Fields to get potential observables from ...</h3></EuiTitle> <EuiSpacer size="s" /> {stateParams.obsFields.map( (field, ix) => ( <EuiFlexGroup key={ix} gutterSize="s"> <EuiFlexItem grow={3}> <EuiFormRow label="Field name"> <EuiSelect options={fieldOptions} value={field.name} onChange={ e => obsSetName(ix, e.target.value) } isInvalid={field.name == ""} /> </EuiFormRow> </EuiFlexItem> <EuiFlexItem grow={2}> <EuiFormRow label="Data type in The Hive"> <EuiSelect options={typesOptions} value={field.type} onChange={ e => obsSetType(ix, e.target.value) } isInvalid={field.type == ""} /> </EuiFormRow> </EuiFlexItem> <EuiFlexItem grow={1}> <EuiFormRow label="Max items shown"> <EuiFieldNumber min={1} max={1000} value={parseInt(field.cnt)} onChange={ e => obsSetCnt(ix, e.target.value) } isInvalid={!(field.cnt > 0)} /> </EuiFormRow> </EuiFlexItem> <EuiFlexItem grow={false}> <EuiFormRow hasEmptyLabelSpace> <EuiButtonIcon iconType="trash" iconSize="m" color="danger" aria-label="Remove field" onClick={ e => obsRemove(ix) } /> </EuiFormRow> </EuiFlexItem> </EuiFlexGroup> ))} <EuiFlexGroup> <EuiFlexItem grow={false}> <EuiButton iconType="plusInCircleFilled" color="primary" onClick={obsAddNew}>Add new field ...</EuiButton> </EuiFlexItem> </EuiFlexGroup> </EuiForm> }