foreman_openscap 12.1.2 → 13.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/foreman_openscap/scap_breakdown_chart.css +15 -0
- data/app/models/foreman_openscap/compliance_status.rb +2 -2
- data/app/views/compliance_hosts/show.html.erb +5 -3
- data/lib/foreman_openscap/engine.rb +1 -1
- data/lib/foreman_openscap/version.rb +1 -1
- data/test/unit/compliance_status_test.rb +50 -0
- data/webpack/components/LineChart/LineChart.fixtures.js +28 -0
- data/webpack/components/LineChart/LineChart.scss +7 -0
- data/webpack/components/LineChart/LineChart.test.js +266 -0
- data/webpack/components/LineChart/LineChartHelpers.js +206 -0
- data/webpack/components/LineChart/index.js +324 -0
- data/webpack/components/OpenscapRemediationWizard/constants.js +3 -0
- data/webpack/components/OpenscapRemediationWizard/steps/Finish.js +2 -2
- data/webpack/components/OpenscapRemediationWizard/steps/ReviewRemediation.js +10 -2
- data/webpack/components/OpenscapRemediationWizard/steps/SnippetSelect.js +3 -1
- data/webpack/global_index.js +3 -1
- data/webpack/index.js +2 -0
- data/webpack/test_setup.js +1 -0
- metadata +8 -19
- data/app/assets/javascripts/foreman_openscap/scap_hosts_show.js +0 -4
- data/webpack/components/ConfirmModal.js +0 -66
- data/webpack/components/ConfirmModal.scss +0 -3
- data/webpack/components/IndexLayout.js +0 -44
- data/webpack/components/IndexTable/IndexTableHelper.js +0 -6
- data/webpack/components/IndexTable/index.js +0 -73
- data/webpack/components/LinkButton.js +0 -42
- data/webpack/components/withDeleteModal.js +0 -51
- data/webpack/components/withLoading.js +0 -107
- data/webpack/helpers/commonHelper.js +0 -1
- data/webpack/helpers/globalIdHelper.js +0 -15
- data/webpack/helpers/mutationHelper.js +0 -68
- data/webpack/helpers/pageParamsHelper.js +0 -31
- data/webpack/helpers/permissionsHelper.js +0 -42
- data/webpack/helpers/tableHelper.js +0 -9
- data/webpack/helpers/toastHelper.js +0 -3
- data/webpack/testHelper.js +0 -127
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useMemo,
|
|
3
|
+
useRef,
|
|
4
|
+
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useState,
|
|
7
|
+
useCallback,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import {
|
|
11
|
+
Chart,
|
|
12
|
+
ChartAxis,
|
|
13
|
+
ChartGroup,
|
|
14
|
+
ChartLine,
|
|
15
|
+
ChartLegend,
|
|
16
|
+
ChartLegendTooltip,
|
|
17
|
+
ChartThemeColor,
|
|
18
|
+
createContainer,
|
|
19
|
+
} from '@patternfly/react-charts';
|
|
20
|
+
|
|
21
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
22
|
+
import {
|
|
23
|
+
formatAxisTick,
|
|
24
|
+
formatTooltipTitle,
|
|
25
|
+
formatYAxisTick,
|
|
26
|
+
getLegendEvents,
|
|
27
|
+
getSeriesOpacity,
|
|
28
|
+
getXAxisTickValues,
|
|
29
|
+
InteractiveLegendLabel,
|
|
30
|
+
InteractiveLegendSymbol,
|
|
31
|
+
XAxisTickLabel,
|
|
32
|
+
} from 'foremanReact/components/common/charts/helpers/LegendHelpers';
|
|
33
|
+
import EmptyState from '../EmptyState';
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
processChartData,
|
|
37
|
+
hasChartData,
|
|
38
|
+
getYTickValues,
|
|
39
|
+
sanitizeChartDimension,
|
|
40
|
+
clampChartPadding,
|
|
41
|
+
getTimeseriesXDomain,
|
|
42
|
+
buildLineChartLegendData,
|
|
43
|
+
formatTooltipValue,
|
|
44
|
+
} from './LineChartHelpers';
|
|
45
|
+
import './LineChart.scss';
|
|
46
|
+
|
|
47
|
+
const DEFAULT_HEIGHT = 350;
|
|
48
|
+
const DEFAULT_WIDTH = 1000;
|
|
49
|
+
|
|
50
|
+
/** Match AreaChart padding so angled x-axis tick labels are not clipped. */
|
|
51
|
+
const CHART_PADDING_BASE = { bottom: 125, right: 170, top: 50 };
|
|
52
|
+
|
|
53
|
+
const getDataClickEvents = onclick => {
|
|
54
|
+
if (!onclick) return null;
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
target: 'data',
|
|
58
|
+
eventHandlers: {
|
|
59
|
+
onClick: () => [
|
|
60
|
+
{
|
|
61
|
+
target: 'data',
|
|
62
|
+
mutation: props => {
|
|
63
|
+
if (props.datum?.name) {
|
|
64
|
+
onclick({ id: props.datum.name });
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const LineChart = ({
|
|
75
|
+
data,
|
|
76
|
+
config,
|
|
77
|
+
noDataMsg,
|
|
78
|
+
xAxisDataLabel,
|
|
79
|
+
onclick,
|
|
80
|
+
id,
|
|
81
|
+
size,
|
|
82
|
+
title: _title,
|
|
83
|
+
unloadData: _unloadData,
|
|
84
|
+
axisOpts: _axisOpts,
|
|
85
|
+
}) => {
|
|
86
|
+
const chartData = useMemo(
|
|
87
|
+
() => processChartData(data, xAxisDataLabel, config),
|
|
88
|
+
[data, xAxisDataLabel, config]
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const CursorVoronoiContainer = useMemo(
|
|
92
|
+
() => createContainer('voronoi', 'cursor'),
|
|
93
|
+
[]
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const [hiddenSeries, setHiddenSeries] = useState(() => new Set());
|
|
97
|
+
const [hoveredSeries, setHoveredSeries] = useState(null);
|
|
98
|
+
const toggleSeries = useCallback(name => {
|
|
99
|
+
setHiddenSeries(prev => {
|
|
100
|
+
const next = new Set(prev);
|
|
101
|
+
if (next.has(name)) next.delete(name);
|
|
102
|
+
else next.add(name);
|
|
103
|
+
return next;
|
|
104
|
+
});
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
const containerRef = useRef(null);
|
|
108
|
+
const [observedSize, setObservedSize] = useState(null);
|
|
109
|
+
|
|
110
|
+
const updateObservedSize = useCallback(() => {
|
|
111
|
+
const el = containerRef.current;
|
|
112
|
+
if (!el) return;
|
|
113
|
+
|
|
114
|
+
const { width, height } = el.getBoundingClientRect();
|
|
115
|
+
if (width > 0 && height > 0) {
|
|
116
|
+
setObservedSize({ width, height });
|
|
117
|
+
}
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
120
|
+
useLayoutEffect(() => {
|
|
121
|
+
updateObservedSize();
|
|
122
|
+
}, [updateObservedSize]);
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
const el = containerRef.current;
|
|
126
|
+
if (!el || typeof ResizeObserver === 'undefined') return undefined;
|
|
127
|
+
|
|
128
|
+
const observer = new ResizeObserver(() => {
|
|
129
|
+
updateObservedSize();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
observer.observe(el);
|
|
133
|
+
return () => observer.disconnect();
|
|
134
|
+
}, [updateObservedSize]);
|
|
135
|
+
|
|
136
|
+
const legendData = useMemo(
|
|
137
|
+
() => (chartData ? buildLineChartLegendData(chartData, hiddenSeries) : []),
|
|
138
|
+
[chartData, hiddenSeries]
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const visibleSeries = useMemo(
|
|
142
|
+
() => chartData?.filter(series => !hiddenSeries.has(series.name)) ?? [],
|
|
143
|
+
[chartData, hiddenSeries]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const tickValues = useMemo(() => getXAxisTickValues(chartData, 6), [
|
|
147
|
+
chartData,
|
|
148
|
+
]);
|
|
149
|
+
const yTickValues = useMemo(() => getYTickValues(chartData, hiddenSeries), [
|
|
150
|
+
chartData,
|
|
151
|
+
hiddenSeries,
|
|
152
|
+
]);
|
|
153
|
+
const timeseriesXDomain = useMemo(
|
|
154
|
+
() =>
|
|
155
|
+
config === 'timeseries' ? getTimeseriesXDomain(chartData) : undefined,
|
|
156
|
+
[chartData, config]
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (!hasChartData(data, xAxisDataLabel) || !chartData) {
|
|
160
|
+
return (
|
|
161
|
+
<EmptyState
|
|
162
|
+
ouiaEmptyStateTitleId="openscap-line-chart-empty-state-title"
|
|
163
|
+
title={noDataMsg}
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const hasExplicitSize = size?.width > 0 && size?.height > 0;
|
|
169
|
+
if (!hasExplicitSize && !observedSize) {
|
|
170
|
+
return <div ref={containerRef} className="line-chart-container" />;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const maxTickLabelLen =
|
|
174
|
+
yTickValues?.length > 0
|
|
175
|
+
? Math.max(...yTickValues.map(t => formatYAxisTick(t).length))
|
|
176
|
+
: formatYAxisTick(0).length;
|
|
177
|
+
const dynamicLeft = maxTickLabelLen * 8 + 50;
|
|
178
|
+
const chartHeight = sanitizeChartDimension(
|
|
179
|
+
size?.height ?? observedSize?.height,
|
|
180
|
+
DEFAULT_HEIGHT
|
|
181
|
+
);
|
|
182
|
+
const chartWidth = sanitizeChartDimension(
|
|
183
|
+
size?.width ?? observedSize?.width,
|
|
184
|
+
DEFAULT_WIDTH
|
|
185
|
+
);
|
|
186
|
+
const padding = clampChartPadding(
|
|
187
|
+
{
|
|
188
|
+
...CHART_PADDING_BASE,
|
|
189
|
+
left: dynamicLeft,
|
|
190
|
+
},
|
|
191
|
+
chartWidth,
|
|
192
|
+
chartHeight
|
|
193
|
+
);
|
|
194
|
+
const chartName = id || 'line-chart';
|
|
195
|
+
const legendEvents = getLegendEvents(chartName, toggleSeries);
|
|
196
|
+
const events = [getDataClickEvents(onclick), legendEvents].filter(Boolean);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div ref={containerRef} className="line-chart-container">
|
|
200
|
+
<Chart
|
|
201
|
+
name={chartName}
|
|
202
|
+
ariaDesc={__('Line chart')}
|
|
203
|
+
themeColor={ChartThemeColor.multi}
|
|
204
|
+
animate={false}
|
|
205
|
+
domainPadding={{ x: [20, 20], y: [10, 10] }}
|
|
206
|
+
scale={config === 'timeseries' ? { x: 'time' } : undefined}
|
|
207
|
+
containerComponent={
|
|
208
|
+
<CursorVoronoiContainer
|
|
209
|
+
cursorDimension="x"
|
|
210
|
+
labels={({ datum }) => formatTooltipValue(datum.y)}
|
|
211
|
+
labelComponent={
|
|
212
|
+
<ChartLegendTooltip
|
|
213
|
+
legendData={legendData}
|
|
214
|
+
title={formatTooltipTitle}
|
|
215
|
+
/>
|
|
216
|
+
}
|
|
217
|
+
mouseFollowTooltips
|
|
218
|
+
voronoiDimension="x"
|
|
219
|
+
voronoiPadding={padding}
|
|
220
|
+
constrainToVisibleArea
|
|
221
|
+
/>
|
|
222
|
+
}
|
|
223
|
+
height={chartHeight}
|
|
224
|
+
width={chartWidth}
|
|
225
|
+
padding={padding}
|
|
226
|
+
legendData={legendData}
|
|
227
|
+
legendOrientation="vertical"
|
|
228
|
+
legendPosition="right"
|
|
229
|
+
legendComponent={
|
|
230
|
+
<ChartLegend
|
|
231
|
+
dataComponent={
|
|
232
|
+
<InteractiveLegendSymbol
|
|
233
|
+
setHoveredSeries={setHoveredSeries}
|
|
234
|
+
toggleSeries={toggleSeries}
|
|
235
|
+
/>
|
|
236
|
+
}
|
|
237
|
+
labelComponent={
|
|
238
|
+
<InteractiveLegendLabel
|
|
239
|
+
hiddenSeries={hiddenSeries}
|
|
240
|
+
hoveredSeries={hoveredSeries}
|
|
241
|
+
setHoveredSeries={setHoveredSeries}
|
|
242
|
+
toggleSeries={toggleSeries}
|
|
243
|
+
/>
|
|
244
|
+
}
|
|
245
|
+
/>
|
|
246
|
+
}
|
|
247
|
+
events={events}
|
|
248
|
+
>
|
|
249
|
+
<ChartAxis
|
|
250
|
+
tickValues={config === 'timeseries' ? tickValues : undefined}
|
|
251
|
+
tickFormat={config === 'timeseries' ? formatAxisTick : undefined}
|
|
252
|
+
domain={timeseriesXDomain}
|
|
253
|
+
tickLabelComponent={
|
|
254
|
+
config === 'timeseries' ? (
|
|
255
|
+
<XAxisTickLabel yAxisLabelOffset={-12} />
|
|
256
|
+
) : (
|
|
257
|
+
undefined
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
style={
|
|
261
|
+
config === 'timeseries'
|
|
262
|
+
? { tickLabels: { angle: -45, verticalAnchor: 'end' } }
|
|
263
|
+
: undefined
|
|
264
|
+
}
|
|
265
|
+
/>
|
|
266
|
+
<ChartAxis
|
|
267
|
+
dependentAxis
|
|
268
|
+
showGrid
|
|
269
|
+
tickValues={yTickValues}
|
|
270
|
+
tickFormat={formatYAxisTick}
|
|
271
|
+
/>
|
|
272
|
+
<ChartGroup>
|
|
273
|
+
{visibleSeries.map(series => (
|
|
274
|
+
<ChartLine
|
|
275
|
+
key={series.name}
|
|
276
|
+
name={series.name}
|
|
277
|
+
data={series.data}
|
|
278
|
+
style={{
|
|
279
|
+
data: {
|
|
280
|
+
...(series.color && { stroke: series.color }),
|
|
281
|
+
opacity: getSeriesOpacity(
|
|
282
|
+
hoveredSeries && hoveredSeries !== series.name
|
|
283
|
+
),
|
|
284
|
+
},
|
|
285
|
+
}}
|
|
286
|
+
/>
|
|
287
|
+
))}
|
|
288
|
+
</ChartGroup>
|
|
289
|
+
</Chart>
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
LineChart.propTypes = {
|
|
295
|
+
data: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
|
296
|
+
config: PropTypes.oneOf(['regular', 'timeseries']),
|
|
297
|
+
noDataMsg: PropTypes.string,
|
|
298
|
+
xAxisDataLabel: PropTypes.string,
|
|
299
|
+
onclick: PropTypes.func,
|
|
300
|
+
id: PropTypes.string,
|
|
301
|
+
size: PropTypes.shape({
|
|
302
|
+
height: PropTypes.number,
|
|
303
|
+
width: PropTypes.number,
|
|
304
|
+
}),
|
|
305
|
+
// Accepted for compatibility with the legacy Foreman wrapper; unused in PF5.
|
|
306
|
+
title: PropTypes.object,
|
|
307
|
+
unloadData: PropTypes.bool,
|
|
308
|
+
axisOpts: PropTypes.object,
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
LineChart.defaultProps = {
|
|
312
|
+
data: undefined,
|
|
313
|
+
config: 'regular',
|
|
314
|
+
noDataMsg: __('No data available'),
|
|
315
|
+
xAxisDataLabel: '',
|
|
316
|
+
onclick: undefined,
|
|
317
|
+
id: undefined,
|
|
318
|
+
size: undefined,
|
|
319
|
+
title: { type: 'percent' },
|
|
320
|
+
unloadData: false,
|
|
321
|
+
axisOpts: {},
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export default LineChart;
|
|
@@ -15,6 +15,9 @@ export const JOB_INVOCATION_API_REQUEST_KEY = 'OPENSCAP_REX_JOB_INVOCATIONS';
|
|
|
15
15
|
export const SNIPPET_SH = 'urn:xccdf:fix:script:sh';
|
|
16
16
|
export const SNIPPET_ANSIBLE = 'urn:xccdf:fix:script:ansible';
|
|
17
17
|
|
|
18
|
+
export const TOOLTIP_COPIED_EXIT_DELAY_MS = 1500;
|
|
19
|
+
export const TOOLTIP_DEFAULT_EXIT_DELAY_MS = 600;
|
|
20
|
+
|
|
18
21
|
export const WIZARD_TITLES = {
|
|
19
22
|
snippetSelect: __('Select snippet'),
|
|
20
23
|
reviewHosts: __('Review hosts'),
|
|
@@ -6,7 +6,7 @@ import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
|
|
|
6
6
|
|
|
7
7
|
import { translate as __ } from 'foremanReact/common/I18n';
|
|
8
8
|
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
9
|
-
import { STATUS } from 'foremanReact/constants';
|
|
9
|
+
import { STATUS, HTTP_STATUS_CODES } from 'foremanReact/constants';
|
|
10
10
|
import { useAPI } from 'foremanReact/common/hooks/API/APIHooks';
|
|
11
11
|
import Loading from 'foremanReact/components/Loading';
|
|
12
12
|
import PermissionDenied from 'foremanReact/components/PermissionDenied';
|
|
@@ -91,7 +91,7 @@ const Finish = ({ onClose }) => {
|
|
|
91
91
|
</Button>
|
|
92
92
|
);
|
|
93
93
|
const errorComponent =
|
|
94
|
-
statusCode ===
|
|
94
|
+
statusCode === HTTP_STATUS_CODES.FORBIDDEN ? (
|
|
95
95
|
<PermissionDenied
|
|
96
96
|
missingPermissions={data?.error?.missing_permissions}
|
|
97
97
|
primaryButton={closeBtn}
|
|
@@ -24,7 +24,11 @@ import {
|
|
|
24
24
|
import OpenscapRemediationWizardContext from '../OpenscapRemediationWizardContext';
|
|
25
25
|
import WizardHeader from '../WizardHeader';
|
|
26
26
|
import ViewSelectedHostsLink from '../ViewSelectedHostsLink';
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
FAIL_RULE_SEARCH,
|
|
29
|
+
TOOLTIP_COPIED_EXIT_DELAY_MS,
|
|
30
|
+
TOOLTIP_DEFAULT_EXIT_DELAY_MS,
|
|
31
|
+
} from '../constants';
|
|
28
32
|
import { findFixBySnippet } from '../helpers';
|
|
29
33
|
|
|
30
34
|
import './ReviewRemediation.scss';
|
|
@@ -78,7 +82,11 @@ const ReviewRemediation = () => {
|
|
|
78
82
|
textId="code-content"
|
|
79
83
|
aria-label="Copy to clipboard"
|
|
80
84
|
onClick={e => onCopyClick(e, snippetText)}
|
|
81
|
-
exitDelay={
|
|
85
|
+
exitDelay={
|
|
86
|
+
copied
|
|
87
|
+
? TOOLTIP_COPIED_EXIT_DELAY_MS
|
|
88
|
+
: TOOLTIP_DEFAULT_EXIT_DELAY_MS
|
|
89
|
+
}
|
|
82
90
|
maxWidth="110px"
|
|
83
91
|
variant="plain"
|
|
84
92
|
onTooltipHidden={() => setCopied(false)}
|
|
@@ -18,6 +18,8 @@ import WizardHeader from '../WizardHeader';
|
|
|
18
18
|
import EmptyState from '../../EmptyState';
|
|
19
19
|
import { errorMsg, supportedRemediationSnippets } from '../helpers';
|
|
20
20
|
|
|
21
|
+
const URN_TAIL_SEGMENTS = -2;
|
|
22
|
+
|
|
21
23
|
const SnippetSelect = () => {
|
|
22
24
|
const {
|
|
23
25
|
fixes,
|
|
@@ -44,7 +46,7 @@ const SnippetSelect = () => {
|
|
|
44
46
|
if (mapped) return mapped;
|
|
45
47
|
|
|
46
48
|
return join(
|
|
47
|
-
map(slice(split(system, ':'),
|
|
49
|
+
map(slice(split(system, ':'), URN_TAIL_SEGMENTS), n => capitalize(n)),
|
|
48
50
|
' '
|
|
49
51
|
);
|
|
50
52
|
};
|
data/webpack/global_index.js
CHANGED
|
@@ -2,9 +2,11 @@ import React from 'react';
|
|
|
2
2
|
import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
|
|
3
3
|
import HostKebabItems from './components/HostExtentions/HostKebabItems';
|
|
4
4
|
|
|
5
|
+
const OPENSCAP_KEBAB_WEIGHT = 400;
|
|
6
|
+
|
|
5
7
|
addGlobalFill(
|
|
6
8
|
'host-details-kebab',
|
|
7
9
|
`openscap-kebab-items`,
|
|
8
10
|
<HostKebabItems key="openscap-host-kebab" />,
|
|
9
|
-
|
|
11
|
+
OPENSCAP_KEBAB_WEIGHT
|
|
10
12
|
);
|
data/webpack/index.js
CHANGED
|
@@ -2,10 +2,12 @@ import componentRegistry from 'foremanReact/components/componentRegistry';
|
|
|
2
2
|
|
|
3
3
|
import RuleSeverity from './components/RuleSeverity';
|
|
4
4
|
import OpenscapRemediationWizard from './components/OpenscapRemediationWizard';
|
|
5
|
+
import LineChart from './components/LineChart';
|
|
5
6
|
|
|
6
7
|
const components = [
|
|
7
8
|
{ name: 'RuleSeverity', type: RuleSeverity },
|
|
8
9
|
{ name: 'OpenscapRemediationWizard', type: OpenscapRemediationWizard },
|
|
10
|
+
{ name: 'OpenscapLineChart', type: LineChart },
|
|
9
11
|
];
|
|
10
12
|
|
|
11
13
|
components.forEach(component => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import 'foremanJSTestSetup';
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_openscap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 13.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- slukasik@redhat.com
|
|
@@ -40,7 +40,6 @@ files:
|
|
|
40
40
|
- app/assets/javascripts/foreman_openscap/policy_dashboard.js
|
|
41
41
|
- app/assets/javascripts/foreman_openscap/policy_edit.js
|
|
42
42
|
- app/assets/javascripts/foreman_openscap/reports.js
|
|
43
|
-
- app/assets/javascripts/foreman_openscap/scap_hosts_show.js
|
|
44
43
|
- app/assets/stylesheets/foreman_openscap/policy.css
|
|
45
44
|
- app/assets/stylesheets/foreman_openscap/policy_dashboard.css
|
|
46
45
|
- app/assets/stylesheets/foreman_openscap/reports.css
|
|
@@ -363,15 +362,14 @@ files:
|
|
|
363
362
|
- test/unit/services/lookup_key_overrider_test.rb
|
|
364
363
|
- test/unit/services/report_dashboard/data_test.rb
|
|
365
364
|
- test/unit/tailoring_file_test.rb
|
|
366
|
-
- webpack/components/ConfirmModal.js
|
|
367
|
-
- webpack/components/ConfirmModal.scss
|
|
368
365
|
- webpack/components/EmptyState.js
|
|
369
366
|
- webpack/components/HostExtentions/HostKebabItems.js
|
|
370
|
-
- webpack/components/IndexLayout.js
|
|
371
367
|
- webpack/components/IndexLayout.scss
|
|
372
|
-
- webpack/components/
|
|
373
|
-
- webpack/components/
|
|
374
|
-
- webpack/components/
|
|
368
|
+
- webpack/components/LineChart/LineChart.fixtures.js
|
|
369
|
+
- webpack/components/LineChart/LineChart.scss
|
|
370
|
+
- webpack/components/LineChart/LineChart.test.js
|
|
371
|
+
- webpack/components/LineChart/LineChartHelpers.js
|
|
372
|
+
- webpack/components/LineChart/index.js
|
|
375
373
|
- webpack/components/OpenscapRemediationWizard/Footer.js
|
|
376
374
|
- webpack/components/OpenscapRemediationWizard/OpenscapRemediationSelectors.js
|
|
377
375
|
- webpack/components/OpenscapRemediationWizard/OpenscapRemediationWizardContext.js
|
|
@@ -395,18 +393,9 @@ files:
|
|
|
395
393
|
- webpack/components/RuleSeverity/i_severity-med.svg
|
|
396
394
|
- webpack/components/RuleSeverity/i_unknown.svg
|
|
397
395
|
- webpack/components/RuleSeverity/index.js
|
|
398
|
-
- webpack/components/withDeleteModal.js
|
|
399
|
-
- webpack/components/withLoading.js
|
|
400
396
|
- webpack/global_index.js
|
|
401
|
-
- webpack/helpers/commonHelper.js
|
|
402
|
-
- webpack/helpers/globalIdHelper.js
|
|
403
|
-
- webpack/helpers/mutationHelper.js
|
|
404
|
-
- webpack/helpers/pageParamsHelper.js
|
|
405
|
-
- webpack/helpers/permissionsHelper.js
|
|
406
|
-
- webpack/helpers/tableHelper.js
|
|
407
|
-
- webpack/helpers/toastHelper.js
|
|
408
397
|
- webpack/index.js
|
|
409
|
-
- webpack/
|
|
398
|
+
- webpack/test_setup.js
|
|
410
399
|
homepage: https://github.com/theforeman/foreman_openscap
|
|
411
400
|
licenses:
|
|
412
401
|
- GPL-3.0
|
|
@@ -425,7 +414,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
425
414
|
- !ruby/object:Gem::Version
|
|
426
415
|
version: '0'
|
|
427
416
|
requirements: []
|
|
428
|
-
rubygems_version: 4.0.
|
|
417
|
+
rubygems_version: 4.0.16
|
|
429
418
|
specification_version: 4
|
|
430
419
|
summary: Foreman plug-in for displaying OpenSCAP audit reports
|
|
431
420
|
test_files:
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { Modal, Button, ModalVariant, Spinner } from '@patternfly/react-core';
|
|
4
|
-
|
|
5
|
-
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
|
-
|
|
7
|
-
import './ConfirmModal.scss';
|
|
8
|
-
|
|
9
|
-
const ConfirmModal = props => {
|
|
10
|
-
const [callMutation, { loading }] = props.prepareMutation();
|
|
11
|
-
|
|
12
|
-
const actions = [
|
|
13
|
-
<Button
|
|
14
|
-
ouiaId={`oscap-conf-modal-${props.record?.id}-confirm`}
|
|
15
|
-
key="confirm"
|
|
16
|
-
variant="primary"
|
|
17
|
-
onClick={() => props.onConfirm(callMutation, props.record.id)}
|
|
18
|
-
isDisabled={loading}
|
|
19
|
-
>
|
|
20
|
-
{__('Confirm')}
|
|
21
|
-
</Button>,
|
|
22
|
-
<Button
|
|
23
|
-
ouiaId={`oscap-conf-modal-${props.record?.id}-cancel`}
|
|
24
|
-
key="cancel"
|
|
25
|
-
variant="link"
|
|
26
|
-
onClick={event => props.onClose()}
|
|
27
|
-
isDisabled={loading}
|
|
28
|
-
>
|
|
29
|
-
{__('Cancel')}
|
|
30
|
-
</Button>,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
if (loading) {
|
|
34
|
-
actions.push(<Spinner key="spinner" size="lg" />);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<Modal
|
|
39
|
-
ouiaId={`oscap-conf-modal-${props.record?.id}`}
|
|
40
|
-
variant={ModalVariant.medium}
|
|
41
|
-
title={props.title}
|
|
42
|
-
isOpen={props.isOpen}
|
|
43
|
-
className="foreman-modal"
|
|
44
|
-
showClose={false}
|
|
45
|
-
actions={actions}
|
|
46
|
-
>
|
|
47
|
-
{props.text}
|
|
48
|
-
</Modal>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
ConfirmModal.propTypes = {
|
|
53
|
-
prepareMutation: PropTypes.func.isRequired,
|
|
54
|
-
onConfirm: PropTypes.func.isRequired,
|
|
55
|
-
record: PropTypes.object,
|
|
56
|
-
onClose: PropTypes.func.isRequired,
|
|
57
|
-
title: PropTypes.string.isRequired,
|
|
58
|
-
isOpen: PropTypes.bool.isRequired,
|
|
59
|
-
text: PropTypes.string.isRequired,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
ConfirmModal.defaultProps = {
|
|
63
|
-
record: null,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export default ConfirmModal;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import { Helmet } from 'react-helmet';
|
|
4
|
-
import ToastsList from 'foremanReact/components/ToastsList';
|
|
5
|
-
import {
|
|
6
|
-
Grid,
|
|
7
|
-
GridItem,
|
|
8
|
-
TextContent,
|
|
9
|
-
Text,
|
|
10
|
-
TextVariants,
|
|
11
|
-
} from '@patternfly/react-core';
|
|
12
|
-
|
|
13
|
-
import './IndexLayout.scss';
|
|
14
|
-
|
|
15
|
-
const IndexLayout = ({ pageTitle, children, contentWidthSpan }) => (
|
|
16
|
-
<React.Fragment>
|
|
17
|
-
<Helmet>
|
|
18
|
-
<title>{pageTitle}</title>
|
|
19
|
-
</Helmet>
|
|
20
|
-
<ToastsList />
|
|
21
|
-
<Grid className="scap-page-grid">
|
|
22
|
-
<GridItem span={12} className="pf-v5-u-pb-xl">
|
|
23
|
-
<TextContent>
|
|
24
|
-
<Text ouiaId="oscap-index-title" component={TextVariants.h1}>
|
|
25
|
-
{pageTitle}
|
|
26
|
-
</Text>
|
|
27
|
-
</TextContent>
|
|
28
|
-
</GridItem>
|
|
29
|
-
<GridItem span={contentWidthSpan}>{children}</GridItem>
|
|
30
|
-
</Grid>
|
|
31
|
-
</React.Fragment>
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
IndexLayout.propTypes = {
|
|
35
|
-
pageTitle: PropTypes.string.isRequired,
|
|
36
|
-
children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]).isRequired,
|
|
37
|
-
contentWidthSpan: PropTypes.number,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
IndexLayout.defaultProps = {
|
|
41
|
-
contentWidthSpan: 12,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export default IndexLayout;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
Table,
|
|
5
|
-
TableHeader,
|
|
6
|
-
TableBody,
|
|
7
|
-
} from '@patternfly/react-table/deprecated';
|
|
8
|
-
import { Flex, FlexItem } from '@patternfly/react-core';
|
|
9
|
-
import Pagination from 'foremanReact/components/Pagination';
|
|
10
|
-
import { refreshPage } from './IndexTableHelper';
|
|
11
|
-
|
|
12
|
-
const IndexTable = ({
|
|
13
|
-
history,
|
|
14
|
-
pagination,
|
|
15
|
-
totalCount,
|
|
16
|
-
toolbarBtns,
|
|
17
|
-
ariaTableLabel,
|
|
18
|
-
ouiaTableId,
|
|
19
|
-
columns,
|
|
20
|
-
...rest
|
|
21
|
-
}) => {
|
|
22
|
-
const handlePerPageSelected = perPage => {
|
|
23
|
-
refreshPage(history, { page: 1, perPage });
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const handlePageSelected = page => {
|
|
27
|
-
refreshPage(history, { ...pagination, page });
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<React.Fragment>
|
|
32
|
-
<Flex className="pf-v5-u-pt-md">
|
|
33
|
-
<FlexItem>{toolbarBtns}</FlexItem>
|
|
34
|
-
<FlexItem align={{ default: 'alignRight' }}>
|
|
35
|
-
<Pagination
|
|
36
|
-
itemCount={totalCount}
|
|
37
|
-
page={pagination.page}
|
|
38
|
-
perPage={pagination.perPage}
|
|
39
|
-
onSetPage={handlePageSelected}
|
|
40
|
-
onPerPageSelect={handlePerPageSelected}
|
|
41
|
-
variant="top"
|
|
42
|
-
/>
|
|
43
|
-
</FlexItem>
|
|
44
|
-
</Flex>
|
|
45
|
-
<Table
|
|
46
|
-
ouiaId={ouiaTableId}
|
|
47
|
-
aria-label={ariaTableLabel}
|
|
48
|
-
cells={columns}
|
|
49
|
-
{...rest}
|
|
50
|
-
variant="compact"
|
|
51
|
-
>
|
|
52
|
-
<TableHeader />
|
|
53
|
-
<TableBody />
|
|
54
|
-
</Table>
|
|
55
|
-
</React.Fragment>
|
|
56
|
-
);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
IndexTable.propTypes = {
|
|
60
|
-
history: PropTypes.object.isRequired,
|
|
61
|
-
pagination: PropTypes.object.isRequired,
|
|
62
|
-
toolbarBtns: PropTypes.node,
|
|
63
|
-
totalCount: PropTypes.number.isRequired,
|
|
64
|
-
ariaTableLabel: PropTypes.string.isRequired,
|
|
65
|
-
ouiaTableId: PropTypes.string.isRequired,
|
|
66
|
-
columns: PropTypes.array.isRequired,
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
IndexTable.defaultProps = {
|
|
70
|
-
toolbarBtns: null,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export default IndexTable;
|