5
5
6
6
import React , { Component } from 'react' ;
7
7
import { RouteComponentProps } from 'react-router-dom' ;
8
- import { EuiSpacer , EuiTitle , EuiText } from '@elastic/eui' ;
8
+ import {
9
+ EuiSpacer ,
10
+ EuiTitle ,
11
+ EuiText ,
12
+ EuiCallOut ,
13
+ EuiAccordion ,
14
+ EuiHorizontalRule ,
15
+ EuiPanel ,
16
+ } from '@elastic/eui' ;
9
17
import FieldMappingsTable from '../components/RequiredFieldMapping' ;
10
18
import { createDetectorSteps } from '../../../utils/constants' ;
11
19
import { ContentPanel } from '../../../../../components/ContentPanel' ;
12
20
import { Detector , FieldMapping } from '../../../../../../models/interfaces' ;
13
- import { EMPTY_FIELD_MAPPINGS } from '../utils/constants' ;
21
+ import { EMPTY_FIELD_MAPPINGS_VIEW } from '../utils/constants' ;
14
22
import { DetectorCreationStep } from '../../../models/types' ;
15
23
import { GetFieldMappingViewResponse } from '../../../../../../server/models/interfaces' ;
16
24
import FieldMappingService from '../../../../../services/FieldMappingService' ;
@@ -49,7 +57,7 @@ export default class ConfigureFieldMapping extends Component<
49
57
} ) ;
50
58
this . state = {
51
59
loading : props . loading || false ,
52
- mappingsData : EMPTY_FIELD_MAPPINGS ,
60
+ mappingsData : EMPTY_FIELD_MAPPINGS_VIEW ,
53
61
createdMappings,
54
62
invalidMappingFieldNames : [ ] ,
55
63
} ;
@@ -70,17 +78,21 @@ export default class ConfigureFieldMapping extends Component<
70
78
Object . keys ( mappingsView . response . properties ) . forEach ( ( ruleFieldName ) => {
71
79
existingMappings [ ruleFieldName ] = mappingsView . response . properties [ ruleFieldName ] . path ;
72
80
} ) ;
73
- this . setState ( { createdMappings : existingMappings , mappingsData : mappingsView . response } ) ;
81
+ this . setState ( {
82
+ createdMappings : existingMappings ,
83
+ mappingsData : {
84
+ ...mappingsView . response ,
85
+ unmapped_field_aliases : [
86
+ 'timestamp' ,
87
+ ...( mappingsView . response . unmapped_field_aliases || [ ] ) ,
88
+ ] ,
89
+ } ,
90
+ } ) ;
74
91
this . updateMappingSharedState ( existingMappings ) ;
75
92
}
76
93
this . setState ( { loading : false } ) ;
77
94
} ;
78
95
79
- validateMappings ( mappings : ruleFieldToIndexFieldMap ) : boolean {
80
- // TODO: Implement validation
81
- return true ; //allFieldsMapped; // && allAliasesUnique;
82
- }
83
-
84
96
/**
85
97
* Returns the fieldName(s) that have duplicate alias assigned to them
86
98
*/
@@ -110,8 +122,7 @@ export default class ConfigureFieldMapping extends Component<
110
122
invalidMappingFieldNames : invalidMappingFieldNames ,
111
123
} ) ;
112
124
this . updateMappingSharedState ( newMappings ) ;
113
- const mappingsValid = this . validateMappings ( newMappings ) ;
114
- this . props . updateDataValidState ( DetectorCreationStep . CONFIGURE_FIELD_MAPPING , mappingsValid ) ;
125
+ this . props . updateDataValidState ( DetectorCreationStep . CONFIGURE_FIELD_MAPPING , true ) ;
115
126
} ;
116
127
117
128
updateMappingSharedState = ( createdMappings : ruleFieldToIndexFieldMap ) => {
@@ -126,42 +137,52 @@ export default class ConfigureFieldMapping extends Component<
126
137
} ;
127
138
128
139
render ( ) {
129
- const { isEdit } = this . props ;
130
140
const { loading, mappingsData, createdMappings, invalidMappingFieldNames } = this . state ;
131
141
const existingMappings : ruleFieldToIndexFieldMap = {
132
142
...createdMappings ,
133
143
} ;
134
- const ruleFields = [ ...( mappingsData . unmapped_field_aliases || [ ] ) ] ;
135
- const indexFields = [ ...( mappingsData . unmapped_index_fields || [ ] ) ] ;
136
144
145
+ // read only data
146
+ const mappedRuleFields : string [ ] = [ ] ;
147
+ const mappedLogFields : string [ ] = [ ] ;
137
148
Object . keys ( mappingsData . properties ) . forEach ( ( ruleFieldName ) => {
138
- existingMappings [ ruleFieldName ] = mappingsData . properties [ ruleFieldName ] . path ;
139
- ruleFields . unshift ( ruleFieldName ) ;
140
- indexFields . unshift ( mappingsData . properties [ ruleFieldName ] . path ) ;
149
+ mappedRuleFields . unshift ( ruleFieldName ) ;
150
+ mappedLogFields . unshift ( mappingsData . properties [ ruleFieldName ] . path ) ;
141
151
} ) ;
142
152
153
+ // edit data
154
+ const ruleFields = [ ...( mappingsData . unmapped_field_aliases || [ ] ) ] ;
155
+ const indexFields = [ ...( mappingsData . unmapped_index_fields || [ ] ) ] ;
156
+
143
157
return (
144
158
< div >
145
- { ! isEdit && (
146
- < >
147
- < EuiTitle size = { 'm' } >
148
- < h3 > { createDetectorSteps [ DetectorCreationStep . CONFIGURE_FIELD_MAPPING ] . title } </ h3 >
149
- </ EuiTitle >
159
+ < EuiTitle size = { 'm' } >
160
+ < h3 > { createDetectorSteps [ DetectorCreationStep . CONFIGURE_FIELD_MAPPING ] . title } </ h3 >
161
+ </ EuiTitle >
150
162
151
- < EuiText size = "s" color = "subdued" >
152
- To perform threat detection, known field names from your log data source are
153
- automatically mapped to rule field names. Additional fields that may require manual
154
- mapping will be shown below.
155
- </ EuiText >
163
+ < EuiText size = "s" color = "subdued" >
164
+ To perform threat detection, known field names from your log data source are automatically
165
+ mapped to rule field names. Additional fields that may require manual mapping will be
166
+ shown below.
167
+ </ EuiText >
156
168
157
- < EuiSpacer size = { 'm' } />
158
- </ >
159
- ) }
169
+ < EuiSpacer size = { 'm' } />
160
170
161
- { ruleFields . length > 0 && (
171
+ { ruleFields . length > 0 ? (
162
172
< >
163
- < ContentPanel title = { `Required field mappings (${ ruleFields . length } )` } titleSize = { 'm' } >
173
+ < EuiCallOut
174
+ title = { `${ ruleFields . length } rule fields may need manual mapping` }
175
+ color = { 'warning' }
176
+ >
177
+ < p >
178
+ To generate accurate findings, we recommend mapping the following security rules
179
+ fields with the log field from your data source.
180
+ </ p >
181
+ </ EuiCallOut >
182
+ < EuiSpacer size = { 'm' } />
183
+ < ContentPanel title = { `Manual field mappings (${ ruleFields . length } )` } titleSize = { 'm' } >
164
184
< FieldMappingsTable < MappingViewType . Edit >
185
+ { ...this . props }
165
186
loading = { loading }
166
187
ruleFields = { ruleFields }
167
188
indexFields = { indexFields }
@@ -171,12 +192,48 @@ export default class ConfigureFieldMapping extends Component<
171
192
invalidMappingFieldNames,
172
193
onMappingCreation : this . onMappingCreation ,
173
194
} }
174
- { ...this . props }
175
195
/>
176
196
</ ContentPanel >
177
197
< EuiSpacer size = { 'm' } />
178
198
</ >
199
+ ) : (
200
+ < >
201
+ < EuiCallOut title = { 'We have automatically mapped all fields' } color = { 'success' } >
202
+ < p >
203
+ Your data source(s) have been mapped with all security rule fields. No action is
204
+ needed.
205
+ </ p >
206
+ </ EuiCallOut >
207
+ < EuiSpacer size = { 'm' } />
208
+ </ >
179
209
) }
210
+
211
+ < EuiPanel >
212
+ < EuiAccordion
213
+ buttonContent = {
214
+ < div data-test-subj = "mapped-fields-btn" >
215
+ < EuiTitle >
216
+ < h4 > { `View mapped fields (${ mappedRuleFields . length } )` } </ h4 >
217
+ </ EuiTitle >
218
+ </ div >
219
+ }
220
+ buttonProps = { { style : { paddingLeft : '10px' , paddingRight : '10px' } } }
221
+ id = { 'mappedFieldsAccordion' }
222
+ initialIsOpen = { false }
223
+ >
224
+ < EuiHorizontalRule margin = { 'xs' } />
225
+ < FieldMappingsTable < MappingViewType . Readonly >
226
+ { ...this . props }
227
+ loading = { loading }
228
+ ruleFields = { mappedRuleFields }
229
+ indexFields = { mappedLogFields }
230
+ mappingProps = { {
231
+ type : MappingViewType . Readonly ,
232
+ } }
233
+ />
234
+ </ EuiAccordion >
235
+ </ EuiPanel >
236
+ < EuiSpacer size = { 'm' } />
180
237
</ div >
181
238
) ;
182
239
}
0 commit comments