foreman_openscap 4.3.2 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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();