foreman_openscap 4.3.2 → 5.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/compliance/arf_reports_controller.rb +0 -6
- data/app/controllers/api/v2/compliance/oval_policies_controller.rb +1 -1
- data/app/graphql/mutations/oval_contents/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/create.rb +33 -0
- data/app/graphql/mutations/oval_policies/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/update.rb +15 -0
- data/app/graphql/types/oval_check.rb +11 -0
- data/app/graphql/types/oval_content.rb +2 -0
- data/app/graphql/types/oval_policy.rb +3 -0
- data/app/helpers/arf_report_dashboard_helper.rb +2 -4
- data/app/helpers/compliance_hosts_helper.rb +1 -1
- data/app/helpers/policies_helper.rb +2 -2
- data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
- data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +16 -0
- data/app/models/concerns/foreman_openscap/policy_common.rb +1 -1
- data/app/models/foreman_openscap/oval_content.rb +2 -0
- data/app/services/foreman_openscap/client_config/base.rb +1 -0
- data/app/services/foreman_openscap/client_config/puppet.rb +6 -2
- data/app/services/foreman_openscap/oval/configure.rb +16 -13
- data/app/services/foreman_openscap/oval/setup.rb +5 -5
- data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
- data/app/views/api/v2/compliance/oval_contents/destroy.json.rabl +3 -0
- data/app/views/arf_reports/_metrics.html.erb +4 -4
- data/app/views/compliance_hosts/show.html.erb +4 -6
- data/app/views/dashboard/_compliance_reports_breakdown_widget.html.erb +4 -3
- data/app/views/policy_dashboard/_policy_chart_widget.html.erb +3 -2
- data/db/migrate/20200117135424_migrate_port_overrides_to_int.rb +2 -1
- data/db/migrate/20201202110213_update_puppet_port_param_type.rb +2 -1
- data/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
- data/lib/foreman_openscap/engine.rb +6 -7
- data/lib/foreman_openscap/version.rb +1 -1
- data/package.json +3 -6
- data/test/functional/api/v2/compliance/oval_reports_controller_test.rb +1 -1
- data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -0
- data/test/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
- data/test/graphql/queries/oval_content_query_test.rb +29 -0
- data/test/helpers/arf_report_dashboard_helper_test.rb +9 -10
- data/test/helpers/policy_dashboard_helper_test.rb +1 -1
- data/test/test_plugin_helper.rb +9 -4
- data/test/unit/policy_test.rb +1 -1
- data/test/unit/services/config_name_service_test.rb +1 -0
- data/test/unit/services/hostgroup_overrider_test.rb +2 -1
- data/test/unit/services/lookup_key_overrider_test.rb +4 -1
- data/test/unit/services/oval/setup_check_test.rb +37 -0
- data/webpack/components/ConfirmModal.js +63 -0
- data/webpack/components/ConfirmModal.scss +3 -0
- data/webpack/components/EditableInput.js +163 -0
- data/webpack/components/EditableInput.scss +3 -0
- data/webpack/components/EmptyState.js +12 -3
- data/webpack/components/IndexLayout.js +11 -4
- data/webpack/components/IndexTable/index.js +21 -16
- data/webpack/components/LinkButton.js +38 -0
- data/webpack/components/withDeleteModal.js +51 -0
- data/webpack/components/withLoading.js +44 -5
- data/webpack/graphql/mutations/createOvalPolicy.gql +22 -0
- data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
- data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
- data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/cves.gql +5 -0
- data/webpack/graphql/queries/hostgroups.gql +14 -0
- data/webpack/graphql/queries/ovalContent.gql +8 -0
- data/webpack/graphql/queries/ovalContents.gql +8 -0
- data/webpack/graphql/queries/ovalPolicies.gql +8 -0
- data/webpack/graphql/queries/ovalPolicy.gql +8 -0
- data/webpack/helpers/formFieldsHelper.js +113 -0
- data/webpack/helpers/globalIdHelper.js +4 -2
- data/webpack/helpers/mutationHelper.js +68 -0
- data/webpack/helpers/pathsHelper.js +10 -3
- data/webpack/helpers/permissionsHelper.js +42 -0
- data/webpack/helpers/toastHelper.js +3 -0
- data/webpack/helpers/toastsHelper.js +3 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +26 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +50 -5
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +98 -77
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +53 -6
- data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
- data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
- data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
- data/{locale/de/foreman_openscap.edit.po → webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js} +0 -0
- data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +18 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +34 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +71 -21
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +34 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js +135 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js +119 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js +107 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js +32 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js +147 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js +172 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js +11 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +1 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js +2 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +87 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +80 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +202 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +50 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +64 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +4 -0
- data/webpack/routes/routes.js +21 -0
- data/webpack/testHelper.js +64 -2
- metadata +80 -37
- data/locale/en_GB/foreman_openscap.edit.po +0 -0
- data/locale/es/foreman_openscap.edit.po +0 -0
- data/locale/fr/foreman_openscap.edit.po +0 -0
- data/locale/gl/foreman_openscap.edit.po +0 -0
- data/locale/it/foreman_openscap.edit.po +0 -0
- data/locale/ja/foreman_openscap.edit.po +0 -0
- data/locale/ko/foreman_openscap.edit.po +0 -0
- data/locale/pt_BR/foreman_openscap.edit.po +0 -0
- data/locale/ru/foreman_openscap.edit.po +0 -0
- data/locale/sv_SE/foreman_openscap.edit.po +0 -0
- data/locale/zh_CN/foreman_openscap.edit.po +0 -0
- data/locale/zh_TW/foreman_openscap.edit.po +0 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
import { translate as __, sprintf } from 'foremanReact/common/I18n';
|
2
|
+
|
3
|
+
export const permissionCheck = (user, permissionsRequired) => {
|
4
|
+
if (permissionsRequired.length === 0) {
|
5
|
+
return { allowed: true };
|
6
|
+
}
|
7
|
+
|
8
|
+
if (!user) {
|
9
|
+
throw new Error(
|
10
|
+
'No user data when loading the page - cannot determine if current user is allowed to view the page.'
|
11
|
+
);
|
12
|
+
}
|
13
|
+
|
14
|
+
if (user.admin) {
|
15
|
+
return { allowed: true };
|
16
|
+
}
|
17
|
+
|
18
|
+
const permList = permissionsRequired.reduce((memo, item) => {
|
19
|
+
const found = user.permissions.nodes.find(
|
20
|
+
permission => permission.name === item
|
21
|
+
);
|
22
|
+
memo.push({ name: item, present: !!found });
|
23
|
+
return memo;
|
24
|
+
}, []);
|
25
|
+
|
26
|
+
if (permList.reduce((memo, item) => memo && item.present, true)) {
|
27
|
+
return { allowed: true, permissions: permList };
|
28
|
+
}
|
29
|
+
|
30
|
+
return { allowed: false, permissions: permList };
|
31
|
+
};
|
32
|
+
|
33
|
+
export const permissionDeniedMsg = permissions => {
|
34
|
+
let msg = __('You are not authorized to view the page. ');
|
35
|
+
if (permissions?.length > 0) {
|
36
|
+
msg += sprintf(
|
37
|
+
__('Request the following permissions from administrator: %s.'),
|
38
|
+
permissions.join(', ')
|
39
|
+
);
|
40
|
+
}
|
41
|
+
return msg;
|
42
|
+
};
|
@@ -4,12 +4,17 @@ import { useQuery } from '@apollo/client';
|
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
|
6
6
|
import IndexLayout from '../../../components/IndexLayout';
|
7
|
+
import LinkButton from '../../../components/LinkButton';
|
7
8
|
import OvalContentsTable from './OvalContentsTable';
|
9
|
+
import { ovalContentsNewPath } from '../../../helpers/pathsHelper';
|
8
10
|
import {
|
9
11
|
useParamsToVars,
|
10
12
|
useCurrentPagination,
|
11
13
|
} from '../../../helpers/pageParamsHelper';
|
14
|
+
|
15
|
+
import { submitDelete, prepareMutation } from '../../../helpers/mutationHelper';
|
12
16
|
import ovalContentsQuery from '../../../graphql/queries/ovalContents.gql';
|
17
|
+
import deleteOvalContentMutation from '../../../graphql/mutations/deleteOvalContent.gql';
|
13
18
|
|
14
19
|
const OvalContentsIndex = props => {
|
15
20
|
const useFetchFn = componentProps =>
|
@@ -33,6 +38,25 @@ const OvalContentsIndex = props => {
|
|
33
38
|
resultPath="ovalContents.nodes"
|
34
39
|
pagination={pagination}
|
35
40
|
emptyStateTitle={__('No OVAL Contents found.')}
|
41
|
+
permissions={['view_oval_contents']}
|
42
|
+
confirmDeleteTitle={__('Delete OVAL Content')}
|
43
|
+
submitDelete={submitDelete}
|
44
|
+
prepareMutation={prepareMutation(
|
45
|
+
props.history,
|
46
|
+
props.showToast,
|
47
|
+
ovalContentsQuery,
|
48
|
+
'deleteOvalContent',
|
49
|
+
__('OVAL Content successfully deleted.'),
|
50
|
+
deleteOvalContentMutation,
|
51
|
+
__('OVAL Content')
|
52
|
+
)}
|
53
|
+
primaryButton={
|
54
|
+
<LinkButton
|
55
|
+
path={ovalContentsNewPath}
|
56
|
+
btnText={__('Create OVAL Content')}
|
57
|
+
/>
|
58
|
+
}
|
59
|
+
shouldRefetch={props.location?.state?.refreshOvalContents}
|
36
60
|
/>
|
37
61
|
</IndexLayout>
|
38
62
|
);
|
@@ -40,6 +64,8 @@ const OvalContentsIndex = props => {
|
|
40
64
|
|
41
65
|
OvalContentsIndex.propTypes = {
|
42
66
|
history: PropTypes.object.isRequired,
|
67
|
+
showToast: PropTypes.func.isRequired,
|
68
|
+
location: PropTypes.object.isRequired,
|
43
69
|
};
|
44
70
|
|
45
71
|
export default OvalContentsIndex;
|
@@ -1,29 +1,73 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import { Button } from '@patternfly/react-core';
|
4
5
|
|
5
6
|
import withLoading from '../../../components/withLoading';
|
7
|
+
import withDeleteModal from '../../../components/withDeleteModal';
|
6
8
|
import IndexTable from '../../../components/IndexTable';
|
9
|
+
import {
|
10
|
+
ovalContentsNewPath,
|
11
|
+
ovalContentsPath,
|
12
|
+
modelPath,
|
13
|
+
} from '../../../helpers/pathsHelper';
|
14
|
+
|
15
|
+
import { linkCell } from '../../../helpers/tableHelper';
|
7
16
|
|
8
17
|
const OvalContentsTable = props => {
|
9
|
-
const columns = [
|
18
|
+
const columns = [
|
19
|
+
{ title: __('Name') },
|
20
|
+
{ title: __('URL') },
|
21
|
+
{ title: __('Original File Name') },
|
22
|
+
];
|
10
23
|
|
11
24
|
const rows = props.ovalContents.map(ovalContent => ({
|
12
|
-
cells: [
|
25
|
+
cells: [
|
26
|
+
{
|
27
|
+
title: linkCell(
|
28
|
+
modelPath(ovalContentsPath, ovalContent),
|
29
|
+
ovalContent.name
|
30
|
+
),
|
31
|
+
},
|
32
|
+
{ title: ovalContent.url || '' },
|
33
|
+
{ title: ovalContent.originalFilename || '' },
|
34
|
+
],
|
13
35
|
ovalContent,
|
14
36
|
}));
|
15
37
|
|
16
|
-
const
|
38
|
+
const actionResolver = (rowData, rest) => {
|
39
|
+
const actions = [];
|
40
|
+
if (rowData.ovalContent.meta.canDestroy) {
|
41
|
+
actions.push({
|
42
|
+
title: __('Delete OVAL Content'),
|
43
|
+
onClick: (event, rowId, rData, extra) => {
|
44
|
+
props.toggleModal(rData.ovalContent);
|
45
|
+
},
|
46
|
+
});
|
47
|
+
}
|
48
|
+
return actions;
|
49
|
+
};
|
50
|
+
|
51
|
+
const createBtn = (
|
52
|
+
<Button
|
53
|
+
onClick={() => props.history.push(ovalContentsNewPath)}
|
54
|
+
variant="primary"
|
55
|
+
aria-label="create_oval_content"
|
56
|
+
>
|
57
|
+
{__('Create OVAL Content')}
|
58
|
+
</Button>
|
59
|
+
);
|
17
60
|
|
18
61
|
return (
|
19
62
|
<IndexTable
|
20
63
|
columns={columns}
|
21
64
|
rows={rows}
|
22
|
-
|
65
|
+
actionResolver={actionResolver}
|
23
66
|
pagination={props.pagination}
|
24
67
|
totalCount={props.totalCount}
|
25
68
|
history={props.history}
|
26
69
|
ariaTableLabel={__('OVAL Contents table')}
|
70
|
+
toolbarBtns={createBtn}
|
27
71
|
/>
|
28
72
|
);
|
29
73
|
};
|
@@ -33,6 +77,7 @@ OvalContentsTable.propTypes = {
|
|
33
77
|
pagination: PropTypes.object.isRequired,
|
34
78
|
totalCount: PropTypes.number.isRequired,
|
35
79
|
history: PropTypes.object.isRequired,
|
80
|
+
toggleModal: PropTypes.func.isRequired,
|
36
81
|
};
|
37
82
|
|
38
|
-
export default withLoading(OvalContentsTable);
|
83
|
+
export default withLoading(withDeleteModal(OvalContentsTable));
|
@@ -0,0 +1,105 @@
|
|
1
|
+
import { admin } from '../../../../testHelper';
|
2
|
+
|
3
|
+
import ovalContentsQuery from '../../../../graphql/queries/ovalContents.gql';
|
4
|
+
import deleteOvalContent from '../../../../graphql/mutations/deleteOvalContent.gql';
|
5
|
+
|
6
|
+
export const firstCall = {
|
7
|
+
data: {
|
8
|
+
ovalContents: {
|
9
|
+
totalCount: 5,
|
10
|
+
nodes: [
|
11
|
+
{
|
12
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z',
|
13
|
+
name: 'ansible OVAL content',
|
14
|
+
url:
|
15
|
+
'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2',
|
16
|
+
originalFilename: '',
|
17
|
+
meta: { canDestroy: true },
|
18
|
+
},
|
19
|
+
{
|
20
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00',
|
21
|
+
name: 'dotnet OVAL content',
|
22
|
+
url:
|
23
|
+
'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
|
24
|
+
originalFilename: '',
|
25
|
+
meta: { canDestroy: true },
|
26
|
+
},
|
27
|
+
],
|
28
|
+
},
|
29
|
+
currentUser: admin,
|
30
|
+
},
|
31
|
+
};
|
32
|
+
|
33
|
+
export const secondCall = {
|
34
|
+
data: {
|
35
|
+
ovalContents: {
|
36
|
+
totalCount: 4,
|
37
|
+
nodes: [
|
38
|
+
{
|
39
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00',
|
40
|
+
name: 'dotnet OVAL content',
|
41
|
+
url:
|
42
|
+
'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
|
43
|
+
originalFilename: '',
|
44
|
+
meta: { canDestroy: true },
|
45
|
+
},
|
46
|
+
{
|
47
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC03',
|
48
|
+
name: 'jboss OVAL content',
|
49
|
+
url: '',
|
50
|
+
originalFilename: 'jboss.oval.xml.bz2',
|
51
|
+
meta: { canDestroy: true },
|
52
|
+
},
|
53
|
+
],
|
54
|
+
},
|
55
|
+
currentUser: admin,
|
56
|
+
},
|
57
|
+
};
|
58
|
+
|
59
|
+
export const deleteMockFactory = (first, second, errors = null) => {
|
60
|
+
let called = false;
|
61
|
+
|
62
|
+
const deleteMocks = [
|
63
|
+
{
|
64
|
+
request: {
|
65
|
+
query: deleteOvalContent,
|
66
|
+
variables: {
|
67
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z',
|
68
|
+
},
|
69
|
+
},
|
70
|
+
result: {
|
71
|
+
data: {
|
72
|
+
deleteOvalContent: {
|
73
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z',
|
74
|
+
errors,
|
75
|
+
},
|
76
|
+
},
|
77
|
+
},
|
78
|
+
},
|
79
|
+
{
|
80
|
+
request: {
|
81
|
+
query: ovalContentsQuery,
|
82
|
+
variables: {
|
83
|
+
first: 2,
|
84
|
+
last: 2,
|
85
|
+
},
|
86
|
+
},
|
87
|
+
newData: () => {
|
88
|
+
if (called && !errors) {
|
89
|
+
return second;
|
90
|
+
} else if (called && errors) {
|
91
|
+
return first;
|
92
|
+
}
|
93
|
+
called = true;
|
94
|
+
return first;
|
95
|
+
},
|
96
|
+
},
|
97
|
+
];
|
98
|
+
return deleteMocks;
|
99
|
+
};
|
100
|
+
|
101
|
+
export const pageParamsHistoryMock = {
|
102
|
+
location: {
|
103
|
+
search: '?page=1&perPage=2',
|
104
|
+
},
|
105
|
+
};
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
3
|
+
import '@testing-library/jest-dom';
|
4
|
+
import userEvent from '@testing-library/user-event';
|
5
|
+
|
6
|
+
import OvalContentsIndex from '../OvalContentsIndex';
|
7
|
+
import {
|
8
|
+
withRouter,
|
9
|
+
withRedux,
|
10
|
+
withMockedProvider,
|
11
|
+
tick,
|
12
|
+
historyMock,
|
13
|
+
} from '../../../../testHelper';
|
14
|
+
import { mocks, noDeleteMocks } from './OvalContentsIndex.fixtures';
|
15
|
+
import {
|
16
|
+
firstCall,
|
17
|
+
secondCall,
|
18
|
+
deleteMockFactory,
|
19
|
+
pageParamsHistoryMock,
|
20
|
+
} from './OvalContentsDestroy.fixtures';
|
21
|
+
|
22
|
+
const TestComponent = withRouter(
|
23
|
+
withRedux(withMockedProvider(OvalContentsIndex))
|
24
|
+
);
|
25
|
+
|
26
|
+
describe('OvalContentsIndex', () => {
|
27
|
+
it('should open and close delete modal', async () => {
|
28
|
+
render(
|
29
|
+
<TestComponent
|
30
|
+
history={historyMock}
|
31
|
+
location={{}}
|
32
|
+
mocks={mocks}
|
33
|
+
showToast={jest.fn()}
|
34
|
+
/>
|
35
|
+
);
|
36
|
+
await waitFor(tick);
|
37
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
38
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
39
|
+
userEvent.click(screen.getByText('Delete OVAL Content'));
|
40
|
+
await waitFor(tick);
|
41
|
+
expect(
|
42
|
+
screen.getByText('Are you sure you want to delete ansible OVAL content?')
|
43
|
+
).toBeInTheDocument();
|
44
|
+
userEvent.click(screen.getByText('Cancel'));
|
45
|
+
await waitFor(tick);
|
46
|
+
expect(
|
47
|
+
screen.queryByText(
|
48
|
+
'Are you sure you want to delete ansible OVAL content?'
|
49
|
+
)
|
50
|
+
).not.toBeInTheDocument();
|
51
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
52
|
+
});
|
53
|
+
it('should delete OVAL content', async () => {
|
54
|
+
const mocked = deleteMockFactory(firstCall, secondCall);
|
55
|
+
const showToast = jest.fn();
|
56
|
+
render(
|
57
|
+
<TestComponent
|
58
|
+
history={pageParamsHistoryMock}
|
59
|
+
location={{}}
|
60
|
+
mocks={mocked}
|
61
|
+
showToast={showToast}
|
62
|
+
/>
|
63
|
+
);
|
64
|
+
await waitFor(tick);
|
65
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
66
|
+
expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument();
|
67
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
68
|
+
userEvent.click(screen.getByText('Delete OVAL Content'));
|
69
|
+
await waitFor(tick);
|
70
|
+
userEvent.click(screen.getByText('Confirm'));
|
71
|
+
await waitFor(tick);
|
72
|
+
expect(showToast).toHaveBeenCalledWith({
|
73
|
+
type: 'success',
|
74
|
+
message: 'OVAL Content successfully deleted.',
|
75
|
+
});
|
76
|
+
await waitFor(tick);
|
77
|
+
expect(screen.queryByText('ansible OVAL content')).not.toBeInTheDocument();
|
78
|
+
expect(screen.getByText('jboss OVAL content')).toBeInTheDocument();
|
79
|
+
});
|
80
|
+
it('should show error when deleting OVAL content fails', async () => {
|
81
|
+
const showToast = jest.fn();
|
82
|
+
render(
|
83
|
+
<TestComponent
|
84
|
+
history={pageParamsHistoryMock}
|
85
|
+
location={{}}
|
86
|
+
mocks={deleteMockFactory(firstCall, secondCall, [
|
87
|
+
{ message: 'is used by first policy', path: ['base'] },
|
88
|
+
{ message: 'is used by second policy', path: ['base'] },
|
89
|
+
])}
|
90
|
+
showToast={showToast}
|
91
|
+
/>
|
92
|
+
);
|
93
|
+
await waitFor(tick);
|
94
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
95
|
+
expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument();
|
96
|
+
userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]);
|
97
|
+
userEvent.click(screen.getByText('Delete OVAL Content'));
|
98
|
+
await waitFor(tick);
|
99
|
+
userEvent.click(screen.getByText('Confirm'));
|
100
|
+
await waitFor(tick);
|
101
|
+
expect(showToast).toHaveBeenCalledWith({
|
102
|
+
type: 'error',
|
103
|
+
message:
|
104
|
+
'There was a following error when deleting OVAL Content: is used by first policy, is used by second policy',
|
105
|
+
});
|
106
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
107
|
+
expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument();
|
108
|
+
});
|
109
|
+
it('should not show delete button when user does not have delete permissions', async () => {
|
110
|
+
render(
|
111
|
+
<TestComponent
|
112
|
+
history={historyMock}
|
113
|
+
location={{}}
|
114
|
+
mocks={noDeleteMocks}
|
115
|
+
showToast={jest.fn()}
|
116
|
+
/>
|
117
|
+
);
|
118
|
+
await waitFor(tick);
|
119
|
+
expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
|
120
|
+
expect(
|
121
|
+
screen.queryByRole('button', { name: 'Actions' })
|
122
|
+
).not.toBeInTheDocument();
|
123
|
+
});
|
124
|
+
});
|
@@ -1,98 +1,119 @@
|
|
1
1
|
import ovalContentsQuery from '../../../../graphql/queries/ovalContents.gql';
|
2
2
|
import { ovalContentsPath } from '../../../../helpers/pathsHelper';
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
mockFactory,
|
5
|
+
admin,
|
6
|
+
intruder,
|
7
|
+
userFactory,
|
8
|
+
} from '../../../../testHelper';
|
4
9
|
|
5
10
|
const ovalContentMockFactory = mockFactory('ovalContents', ovalContentsQuery);
|
6
11
|
|
7
|
-
|
12
|
+
const viewer = userFactory('viewer', [
|
8
13
|
{
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
first: 20,
|
13
|
-
last: 20,
|
14
|
-
},
|
15
|
-
},
|
16
|
-
result: {
|
17
|
-
data: {
|
18
|
-
ovalContents: {
|
19
|
-
totalCount: 4,
|
20
|
-
nodes: [
|
21
|
-
{
|
22
|
-
id: 'abc',
|
23
|
-
name: 'ansible OVAL content',
|
24
|
-
url:
|
25
|
-
'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2',
|
26
|
-
originalFilename: '',
|
27
|
-
},
|
28
|
-
{
|
29
|
-
id: 'bcd',
|
30
|
-
name: 'dotnet OVAL content',
|
31
|
-
url:
|
32
|
-
'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
|
33
|
-
originalFilename: '',
|
34
|
-
},
|
35
|
-
{
|
36
|
-
id: 'cde',
|
37
|
-
name: 'jboss OVAL content',
|
38
|
-
url: '',
|
39
|
-
originalFilename: 'jboss.oval.xml.bz2',
|
40
|
-
},
|
41
|
-
{
|
42
|
-
id: 'def',
|
43
|
-
name: 'openshift OVAL content',
|
44
|
-
url: '',
|
45
|
-
originalFilename: 'openshift.oval.xml.bz2',
|
46
|
-
},
|
47
|
-
],
|
48
|
-
},
|
49
|
-
},
|
50
|
-
},
|
14
|
+
__typename: 'Permission',
|
15
|
+
id: 'MDE6UGVybWlzc2lvbi0yOTY=',
|
16
|
+
name: 'view_oval_contents',
|
51
17
|
},
|
18
|
+
]);
|
19
|
+
|
20
|
+
const firstContent = (meta = { canDestroy: true }) => ({
|
21
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z',
|
22
|
+
name: 'ansible OVAL content',
|
23
|
+
url:
|
24
|
+
'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2',
|
25
|
+
originalFilename: '',
|
26
|
+
meta,
|
27
|
+
});
|
28
|
+
|
29
|
+
const secondContent = (meta = { canDestroy: true }) => ({
|
30
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00',
|
31
|
+
name: 'dotnet OVAL content',
|
32
|
+
url: 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
|
33
|
+
originalFilename: '',
|
34
|
+
meta,
|
35
|
+
});
|
36
|
+
|
37
|
+
const thirdContent = (meta = { canDestroy: true }) => ({
|
38
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC03',
|
39
|
+
name: 'jboss OVAL content',
|
40
|
+
url: '',
|
41
|
+
originalFilename: 'jboss.oval.xml.bz2',
|
42
|
+
meta,
|
43
|
+
});
|
44
|
+
|
45
|
+
const fourthContent = (meta = { canDestroy: true }) => ({
|
46
|
+
id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0zMw==',
|
47
|
+
name: 'openshift OVAL content',
|
48
|
+
url: '',
|
49
|
+
originalFilename: 'openshift.oval.xml.bz2',
|
50
|
+
meta,
|
51
|
+
});
|
52
|
+
|
53
|
+
const ovalContentNodes = [
|
54
|
+
firstContent(),
|
55
|
+
secondContent(),
|
56
|
+
thirdContent(),
|
57
|
+
fourthContent(),
|
52
58
|
];
|
53
59
|
|
54
|
-
export const
|
60
|
+
export const ovalContents = {
|
61
|
+
totalCount: ovalContentNodes.length,
|
62
|
+
nodes: ovalContentNodes,
|
63
|
+
};
|
64
|
+
|
65
|
+
export const mocks = ovalContentMockFactory(
|
66
|
+
{ first: 20, last: 20 },
|
55
67
|
{
|
56
|
-
|
57
|
-
|
58
|
-
variables: {
|
59
|
-
first: 10,
|
60
|
-
last: 5,
|
61
|
-
},
|
62
|
-
},
|
63
|
-
result: {
|
64
|
-
data: {
|
65
|
-
ovalContents: {
|
66
|
-
totalCount: 7,
|
67
|
-
nodes: [
|
68
|
-
{
|
69
|
-
id: 'bcd',
|
70
|
-
name: 'dotnet OVAL content',
|
71
|
-
url:
|
72
|
-
'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
|
73
|
-
originalFilename: '',
|
74
|
-
},
|
75
|
-
{
|
76
|
-
id: 'def',
|
77
|
-
name: 'openshift OVAL content',
|
78
|
-
url: '',
|
79
|
-
originalFilename: 'openshift.oval.xml.bz2',
|
80
|
-
},
|
81
|
-
],
|
82
|
-
},
|
83
|
-
},
|
84
|
-
},
|
68
|
+
totalCount: 4,
|
69
|
+
nodes: [firstContent(), secondContent(), thirdContent(), fourthContent()],
|
85
70
|
},
|
86
|
-
|
71
|
+
{ currentUser: admin }
|
72
|
+
);
|
73
|
+
|
74
|
+
export const unpagedMocks = ovalContentMockFactory({}, ovalContents, {
|
75
|
+
currentUser: admin,
|
76
|
+
});
|
77
|
+
|
78
|
+
export const paginatedMocks = ovalContentMockFactory(
|
79
|
+
{ first: 10, last: 5 },
|
80
|
+
{ totalCount: 7, nodes: [secondContent(), fourthContent()] },
|
81
|
+
{ currentUser: admin }
|
82
|
+
);
|
87
83
|
|
88
84
|
export const emptyMocks = ovalContentMockFactory(
|
89
85
|
{ first: 20, last: 20 },
|
90
|
-
{ totalCount: 0, nodes: [] }
|
86
|
+
{ totalCount: 0, nodes: [] },
|
87
|
+
{ currentUser: admin }
|
91
88
|
);
|
92
89
|
export const errorMocks = ovalContentMockFactory(
|
93
90
|
{ first: 20, last: 20 },
|
94
91
|
{ totalCount: 0, nodes: [] },
|
95
|
-
[{ message: 'Something very bad happened.' }]
|
92
|
+
{ errors: [{ message: 'Something very bad happened.' }], currentUser: admin }
|
93
|
+
);
|
94
|
+
|
95
|
+
export const viewerMocks = ovalContentMockFactory(
|
96
|
+
{ first: 20, last: 20 },
|
97
|
+
ovalContents,
|
98
|
+
{ currentUser: viewer }
|
99
|
+
);
|
100
|
+
|
101
|
+
export const unauthorizedMocks = ovalContentMockFactory(
|
102
|
+
{ first: 20, last: 20 },
|
103
|
+
ovalContents,
|
104
|
+
{ currentUser: intruder }
|
105
|
+
);
|
106
|
+
|
107
|
+
export const noDeleteMocks = ovalContentMockFactory(
|
108
|
+
{ first: 20, last: 20 },
|
109
|
+
{
|
110
|
+
totalCount: 2,
|
111
|
+
nodes: [
|
112
|
+
firstContent({ canDestroy: false }),
|
113
|
+
secondContent({ canDestroy: false }),
|
114
|
+
],
|
115
|
+
},
|
116
|
+
{ currentUser: admin }
|
96
117
|
);
|
97
118
|
|
98
119
|
export const pushMock = jest.fn();
|