foreman_openscap 4.3.2 → 5.2.0

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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/compliance/arf_reports_controller.rb +0 -6
  3. data/app/controllers/api/v2/compliance/oval_policies_controller.rb +1 -1
  4. data/app/graphql/mutations/oval_contents/delete.rb +9 -0
  5. data/app/graphql/mutations/oval_policies/create.rb +33 -0
  6. data/app/graphql/mutations/oval_policies/delete.rb +9 -0
  7. data/app/graphql/mutations/oval_policies/update.rb +15 -0
  8. data/app/graphql/types/oval_check.rb +11 -0
  9. data/app/graphql/types/oval_content.rb +2 -0
  10. data/app/graphql/types/oval_policy.rb +3 -0
  11. data/app/helpers/arf_report_dashboard_helper.rb +2 -4
  12. data/app/helpers/compliance_hosts_helper.rb +1 -1
  13. data/app/helpers/policies_helper.rb +2 -2
  14. data/app/models/concerns/foreman_openscap/data_stream_content.rb +1 -1
  15. data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
  16. data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +16 -0
  17. data/app/models/foreman_openscap/arf_report.rb +1 -1
  18. data/app/models/foreman_openscap/oval_content.rb +2 -0
  19. data/app/services/foreman_openscap/client_config/base.rb +1 -0
  20. data/app/services/foreman_openscap/client_config/puppet.rb +6 -2
  21. data/app/services/foreman_openscap/oval/configure.rb +16 -13
  22. data/app/services/foreman_openscap/oval/setup.rb +5 -5
  23. data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
  24. data/app/views/api/v2/compliance/oval_contents/destroy.json.rabl +3 -0
  25. data/app/views/arf_reports/_metrics.html.erb +4 -4
  26. data/app/views/compliance_hosts/show.html.erb +4 -6
  27. data/app/views/dashboard/_compliance_reports_breakdown_widget.html.erb +4 -3
  28. data/app/views/policy_dashboard/_policy_chart_widget.html.erb +3 -2
  29. data/db/migrate/20200117135424_migrate_port_overrides_to_int.rb +2 -1
  30. data/db/migrate/20201202110213_update_puppet_port_param_type.rb +2 -1
  31. data/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
  32. data/lib/foreman_openscap/engine.rb +8 -9
  33. data/lib/foreman_openscap/version.rb +1 -1
  34. data/package.json +3 -6
  35. data/test/functional/api/v2/compliance/oval_reports_controller_test.rb +1 -1
  36. data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -0
  37. data/test/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
  38. data/test/graphql/queries/oval_content_query_test.rb +29 -0
  39. data/test/helpers/arf_report_dashboard_helper_test.rb +9 -10
  40. data/test/helpers/policy_dashboard_helper_test.rb +1 -1
  41. data/test/test_plugin_helper.rb +9 -4
  42. data/test/unit/policy_test.rb +1 -1
  43. data/test/unit/services/config_name_service_test.rb +1 -0
  44. data/test/unit/services/hostgroup_overrider_test.rb +2 -1
  45. data/test/unit/services/lookup_key_overrider_test.rb +4 -1
  46. data/test/unit/services/oval/setup_check_test.rb +37 -0
  47. data/webpack/components/ConfirmModal.js +63 -0
  48. data/webpack/components/ConfirmModal.scss +3 -0
  49. data/webpack/components/EditableInput.js +163 -0
  50. data/webpack/components/EditableInput.scss +3 -0
  51. data/webpack/components/EmptyState.js +12 -3
  52. data/webpack/components/IndexLayout.js +11 -4
  53. data/webpack/components/IndexTable/index.js +21 -16
  54. data/webpack/components/LinkButton.js +38 -0
  55. data/webpack/components/withDeleteModal.js +51 -0
  56. data/webpack/components/withLoading.js +44 -5
  57. data/webpack/graphql/mutations/createOvalPolicy.gql +22 -0
  58. data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
  59. data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
  60. data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
  61. data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
  62. data/webpack/graphql/queries/cves.gql +5 -0
  63. data/webpack/graphql/queries/hostgroups.gql +14 -0
  64. data/webpack/graphql/queries/ovalContent.gql +8 -0
  65. data/webpack/graphql/queries/ovalContents.gql +8 -0
  66. data/webpack/graphql/queries/ovalPolicies.gql +8 -0
  67. data/webpack/graphql/queries/ovalPolicy.gql +8 -0
  68. data/webpack/helpers/formFieldsHelper.js +113 -0
  69. data/webpack/helpers/globalIdHelper.js +4 -2
  70. data/webpack/helpers/mutationHelper.js +68 -0
  71. data/webpack/helpers/pathsHelper.js +10 -3
  72. data/webpack/helpers/permissionsHelper.js +42 -0
  73. data/webpack/helpers/toastHelper.js +3 -0
  74. data/webpack/helpers/toastsHelper.js +3 -0
  75. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +26 -0
  76. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +50 -5
  77. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
  78. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
  79. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +98 -77
  80. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +53 -6
  81. data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
  82. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
  83. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
  84. data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
  85. data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
  86. data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
  87. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
  88. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
  89. data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js +0 -0
  90. data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
  91. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +18 -2
  92. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +34 -4
  93. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
  94. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
  95. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +71 -21
  96. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +34 -2
  97. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
  98. data/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js +135 -0
  99. data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js +119 -0
  100. data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js +107 -0
  101. data/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js +32 -0
  102. data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js +147 -0
  103. data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js +172 -0
  104. data/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js +11 -0
  105. data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +1 -0
  106. data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js +2 -2
  107. data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +87 -0
  108. data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
  109. data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
  110. data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
  111. data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +80 -2
  112. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
  113. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +202 -0
  114. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +50 -4
  115. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +64 -4
  116. data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +4 -0
  117. data/webpack/routes/routes.js +21 -0
  118. data/webpack/testHelper.js +64 -2
  119. metadata +63 -7
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ import { addToast } from 'foremanReact/components/ToastsList';
2
+
3
+ export const showToast = dispatch => toast => dispatch(addToast(toast));
@@ -0,0 +1,3 @@
1
+ import { addToast } from 'foremanReact/redux/actions/toasts';
2
+
3
+ export const showToast = dispatch => toast => dispatch(addToast(toast));
@@ -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 = [{ title: __('Name') }];
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: [{ title: ovalContent.name }],
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 actions = [];
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
- actions={actions}
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 { mockFactory } from '../../../../testHelper';
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
- export const mocks = [
12
+ const viewer = userFactory('viewer', [
8
13
  {
9
- request: {
10
- query: ovalContentsQuery,
11
- variables: {
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 paginatedMocks = [
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
- request: {
57
- query: ovalContentsQuery,
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();