foreman_openscap 13.1.3 → 13.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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/compliance/hosts_bulk_actions_controller.rb +38 -1
- data/app/controllers/policies_controller.rb +1 -1
- data/config/routes.rb +2 -0
- data/lib/foreman_openscap/engine.rb +3 -1
- data/lib/foreman_openscap/version.rb +1 -1
- data/test/functional/api/v2/compliance/hosts_bulk_actions_controller_test.rb +175 -1
- data/webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyMenuItems.js +49 -0
- data/webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyModal.js +304 -0
- data/webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyModalScene.js +65 -0
- data/webpack/components/BulkActions/compliancePolicy/__tests__/actions.test.js +121 -0
- data/webpack/components/BulkActions/compliancePolicy/actions.js +32 -0
- data/webpack/components/BulkActions/compliancePolicy/constants.js +8 -0
- data/webpack/global_index.js +27 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c87d1f5b2553990c35d7307969efd773d1a29a29f7871f91aafca638789c746
|
|
4
|
+
data.tar.gz: c9bebdf6c2008237cad80975e5c675e708b031f4167c1b75abcf28a1d77d7e21
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb4f58ffa8e0d4b7ee9142be81f4193fce8f72f130ebef4ff0d1fc2f54b980983b27835cbe020b1bc2ba11cf5e89d3e56bb2dcdfce50b567d46f5162c275733b
|
|
7
|
+
data.tar.gz: 4773b95b988c447e2db42f6974b0dc893ce8fcb4350615f124698db83a203f26cdc428fcebd25591635b0841ad5d7441130548fd6f3b85457979e9f5d624dc27
|
|
@@ -7,9 +7,10 @@ module Api::V2
|
|
|
7
7
|
render_error(:custom_error, :status => :unprocessable_entity, :locals => { :message => exception.message })
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
before_action :find_editable_hosts, only: [:change_openscap_proxy]
|
|
10
|
+
before_action :find_editable_hosts, only: [:change_openscap_proxy, :assign_compliance_policy, :unassign_compliance_policy]
|
|
11
11
|
before_action :find_openscap_proxy, only: [:change_openscap_proxy]
|
|
12
12
|
before_action :validate_openscap_proxy_feature, only: [:change_openscap_proxy]
|
|
13
|
+
before_action :find_compliance_policy, only: [:assign_compliance_policy, :unassign_compliance_policy]
|
|
13
14
|
|
|
14
15
|
def_param_group :bulk_host_ids do
|
|
15
16
|
param :included, Hash, :desc => N_("Hosts to include in the action"), :required => true, :action_aware => true do
|
|
@@ -23,6 +24,33 @@ module Api::V2
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
api :PUT, "/compliance/hosts/bulk/assign_compliance_policy", N_("Assign compliance policy to multiple hosts")
|
|
28
|
+
param_group :bulk_host_ids
|
|
29
|
+
param :policy_id, :number, :required => true, :desc => N_("ID of the compliance policy to assign to the hosts")
|
|
30
|
+
def assign_compliance_policy
|
|
31
|
+
@policy.host_ids = @policy.host_ids + @hosts.pluck(:id)
|
|
32
|
+
if @policy.save
|
|
33
|
+
message = _("Assigned with compliance policy: %s") % @policy.name
|
|
34
|
+
process_response(true, {
|
|
35
|
+
:message => n_("Updated host: #{message}", "Updated hosts: #{message}", @hosts.count),
|
|
36
|
+
})
|
|
37
|
+
else
|
|
38
|
+
render_error(:custom_error, :status => :unprocessable_entity,
|
|
39
|
+
:locals => { :message => @policy.errors.full_messages.to_sentence })
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
api :PUT, "/compliance/hosts/bulk/unassign_compliance_policy", N_("Unassign compliance policy from multiple hosts")
|
|
44
|
+
param_group :bulk_host_ids
|
|
45
|
+
param :policy_id, :number, :required => true, :desc => N_("ID of the compliance policy to unassign from the hosts")
|
|
46
|
+
def unassign_compliance_policy
|
|
47
|
+
@policy.unassign_hosts(@hosts)
|
|
48
|
+
message = _("Unassigned from compliance policy '%s'") % @policy.name
|
|
49
|
+
process_response(true, {
|
|
50
|
+
:message => n_("Updated host: #{message}", "Updated hosts: #{message}", @hosts.count),
|
|
51
|
+
})
|
|
52
|
+
end
|
|
53
|
+
|
|
26
54
|
api :PUT, "/hosts/bulk/change_openscap_proxy", N_("Assign OpenSCAP Proxy to multiple hosts")
|
|
27
55
|
param_group :bulk_host_ids
|
|
28
56
|
param :openscap_proxy_id, :number, :required => true, :desc => N_("ID of the OpenSCAP Proxy to assign to the hosts")
|
|
@@ -84,6 +112,15 @@ module Api::V2
|
|
|
84
112
|
render_error(:custom_error, :status => :unprocessable_entity,
|
|
85
113
|
:locals => { :message => _("The selected OpenSCAP Proxy does not have the OpenSCAP feature enabled.") })
|
|
86
114
|
end
|
|
115
|
+
|
|
116
|
+
def find_compliance_policy
|
|
117
|
+
@policy = ::ForemanOpenscap::Policy.authorized(:assign_policies)
|
|
118
|
+
.find_by(:id => params.require(:policy_id))
|
|
119
|
+
return if @policy
|
|
120
|
+
|
|
121
|
+
render_error(:custom_error, :status => :unprocessable_entity,
|
|
122
|
+
:locals => { :message => _("Compliance policy with id %s not found") % params[:policy_id] })
|
|
123
|
+
end
|
|
87
124
|
end
|
|
88
125
|
end
|
|
89
126
|
end
|
|
@@ -83,7 +83,7 @@ class PoliciesController < ApplicationController
|
|
|
83
83
|
if policy.save
|
|
84
84
|
success _("Updated hosts: Assigned with compliance policy: %s") % policy.name
|
|
85
85
|
# We prefer to go back as this does not lose the current search
|
|
86
|
-
|
|
86
|
+
redirect_to hosts_path
|
|
87
87
|
else
|
|
88
88
|
return process_error :object => policy, :redirect => hosts_path
|
|
89
89
|
end
|
data/config/routes.rb
CHANGED
|
@@ -105,6 +105,8 @@ Rails.application.routes.draw do
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
match 'hosts/bulk/change_openscap_proxy', :to => 'compliance/hosts_bulk_actions#change_openscap_proxy', :via => [:put]
|
|
108
|
+
match 'compliance/hosts/bulk/assign_compliance_policy', :to => 'compliance/hosts_bulk_actions#assign_compliance_policy', :via => [:put]
|
|
109
|
+
match 'compliance/hosts/bulk/unassign_compliance_policy', :to => 'compliance/hosts_bulk_actions#unassign_compliance_policy', :via => [:put]
|
|
108
110
|
end
|
|
109
111
|
end
|
|
110
112
|
end
|
|
@@ -76,7 +76,9 @@ module ForemanOpenscap
|
|
|
76
76
|
:resource_type => 'ForemanOpenscap::Policy'
|
|
77
77
|
permission :assign_policies, { :policies => %i[select_multiple_hosts update_multiple_hosts
|
|
78
78
|
disassociate_multiple_hosts
|
|
79
|
-
remove_policy_from_multiple_hosts]
|
|
79
|
+
remove_policy_from_multiple_hosts],
|
|
80
|
+
'api/v2/compliance/hosts_bulk_actions' => %i[assign_compliance_policy
|
|
81
|
+
unassign_compliance_policy] },
|
|
80
82
|
:resource_type => 'ForemanOpenscap::Policy'
|
|
81
83
|
permission :view_scap_contents, { :scap_contents => %i[index show auto_complete_search],
|
|
82
84
|
'api/v2/compliance/scap_contents' => %i[index show xml],
|
|
@@ -3,7 +3,7 @@ require 'test_plugin_helper'
|
|
|
3
3
|
class Api::V2::Compliance::HostsBulkActionsControllerTest < ActionController::TestCase
|
|
4
4
|
tests Api::V2::Compliance::HostsBulkActionsController
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
setup do
|
|
7
7
|
as_admin do
|
|
8
8
|
@organization = FactoryBot.create(:organization)
|
|
9
9
|
@location = FactoryBot.create(:location)
|
|
@@ -17,6 +17,10 @@ class Api::V2::Compliance::HostsBulkActionsControllerTest < ActionController::Te
|
|
|
17
17
|
:organization => @organization,
|
|
18
18
|
:location => @location)
|
|
19
19
|
@host_ids = [@host1.id, @host2.id]
|
|
20
|
+
@policy = FactoryBot.create(:policy,
|
|
21
|
+
:organizations => [@organization],
|
|
22
|
+
:locations => [@location])
|
|
23
|
+
@policy.scap_content.update(:organization_ids => [@organization.id], :location_ids => [@location.id])
|
|
20
24
|
end
|
|
21
25
|
end
|
|
22
26
|
|
|
@@ -129,4 +133,174 @@ class Api::V2::Compliance::HostsBulkActionsControllerTest < ActionController::Te
|
|
|
129
133
|
refute_match(/Successfully updated/, response['error']['message'])
|
|
130
134
|
assert_equal @host_ids.sort, response['error']['failed_host_ids'].sort
|
|
131
135
|
end
|
|
136
|
+
|
|
137
|
+
test "should assign compliance policy to selected hosts" do
|
|
138
|
+
put :assign_compliance_policy,
|
|
139
|
+
params: valid_bulk_params.merge(:policy_id => @policy.id),
|
|
140
|
+
session: set_session_user
|
|
141
|
+
|
|
142
|
+
assert_response :success
|
|
143
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
144
|
+
assert_match(/Updated hosts: Assigned with compliance policy/, response['message'])
|
|
145
|
+
assert_includes response['message'], @policy.name
|
|
146
|
+
|
|
147
|
+
@policy.reload
|
|
148
|
+
assert_includes @policy.hosts.map(&:id), @host1.id
|
|
149
|
+
assert_includes @policy.hosts.map(&:id), @host2.id
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
test "should assign compliance policy to a single host" do
|
|
153
|
+
put :assign_compliance_policy,
|
|
154
|
+
params: valid_bulk_params([@host1.id]).merge(:policy_id => @policy.id),
|
|
155
|
+
session: set_session_user
|
|
156
|
+
|
|
157
|
+
assert_response :success
|
|
158
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
159
|
+
assert_match(/Updated host: Assigned with compliance policy/, response['message'])
|
|
160
|
+
|
|
161
|
+
@policy.reload
|
|
162
|
+
assert_includes @policy.hosts.map(&:id), @host1.id
|
|
163
|
+
refute_includes @policy.hosts.map(&:id), @host2.id
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
test "should require policy_id for assign" do
|
|
167
|
+
put :assign_compliance_policy,
|
|
168
|
+
params: valid_bulk_params,
|
|
169
|
+
session: set_session_user
|
|
170
|
+
|
|
171
|
+
assert_response :unprocessable_entity
|
|
172
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
173
|
+
assert_match(/policy_id/, response['error']['message'])
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
test "should return error when policy is not found for assign" do
|
|
177
|
+
put :assign_compliance_policy,
|
|
178
|
+
params: valid_bulk_params.merge(:policy_id => 0),
|
|
179
|
+
session: set_session_user
|
|
180
|
+
|
|
181
|
+
assert_response :unprocessable_entity
|
|
182
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
183
|
+
assert_match(/Compliance policy with id .* not found/, response['error']['message'])
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
test "should unassign compliance policy from selected hosts" do
|
|
187
|
+
as_admin do
|
|
188
|
+
@host1.policies = [@policy]
|
|
189
|
+
@host1.save!
|
|
190
|
+
@host2.policies = [@policy]
|
|
191
|
+
@host2.save!
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
put :unassign_compliance_policy,
|
|
195
|
+
params: valid_bulk_params.merge(:policy_id => @policy.id),
|
|
196
|
+
session: set_session_user
|
|
197
|
+
|
|
198
|
+
assert_response :success
|
|
199
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
200
|
+
assert_match(/Updated hosts: Unassigned from compliance policy/, response['message'])
|
|
201
|
+
assert_includes response['message'], @policy.name
|
|
202
|
+
|
|
203
|
+
@policy.reload
|
|
204
|
+
refute_includes @policy.hosts.map(&:id), @host1.id
|
|
205
|
+
refute_includes @policy.hosts.map(&:id), @host2.id
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
test "should unassign compliance policy from a single host" do
|
|
209
|
+
as_admin do
|
|
210
|
+
@host1.policies = [@policy]
|
|
211
|
+
@host1.save!
|
|
212
|
+
@host2.policies = [@policy]
|
|
213
|
+
@host2.save!
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
put :unassign_compliance_policy,
|
|
217
|
+
params: valid_bulk_params([@host1.id]).merge(:policy_id => @policy.id),
|
|
218
|
+
session: set_session_user
|
|
219
|
+
|
|
220
|
+
assert_response :success
|
|
221
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
222
|
+
assert_match(/Updated host: Unassigned from compliance policy/, response['message'])
|
|
223
|
+
|
|
224
|
+
@policy.reload
|
|
225
|
+
refute_includes @policy.hosts.map(&:id), @host1.id
|
|
226
|
+
assert_includes @policy.hosts.map(&:id), @host2.id
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
test "should require policy_id for unassign" do
|
|
230
|
+
put :unassign_compliance_policy,
|
|
231
|
+
params: valid_bulk_params,
|
|
232
|
+
session: set_session_user
|
|
233
|
+
|
|
234
|
+
assert_response :unprocessable_entity
|
|
235
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
236
|
+
assert_match(/policy_id/, response['error']['message'])
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
test "should return error when policy is not found for unassign" do
|
|
240
|
+
put :unassign_compliance_policy,
|
|
241
|
+
params: valid_bulk_params.merge(:policy_id => 0),
|
|
242
|
+
session: set_session_user
|
|
243
|
+
|
|
244
|
+
assert_response :unprocessable_entity
|
|
245
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
246
|
+
assert_match(/Compliance policy with id .* not found/, response['error']['message'])
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
context "forbidden user" do
|
|
250
|
+
setup do
|
|
251
|
+
@user = FactoryBot.create(:user, :admin => false,
|
|
252
|
+
:organizations => [@organization],
|
|
253
|
+
:locations => [@location])
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
test "should forbid assign compliance policy without assign_policies permission" do
|
|
257
|
+
setup_user('edit', 'hosts', nil, @user)
|
|
258
|
+
|
|
259
|
+
put :assign_compliance_policy,
|
|
260
|
+
params: valid_bulk_params.merge(:policy_id => @policy.id),
|
|
261
|
+
session: set_session_user(@user)
|
|
262
|
+
|
|
263
|
+
assert_forbidden_missing_permission('assign_policies')
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
test "should forbid unassign compliance policy without assign_policies permission" do
|
|
267
|
+
setup_user('edit', 'hosts', nil, @user)
|
|
268
|
+
|
|
269
|
+
put :unassign_compliance_policy,
|
|
270
|
+
params: valid_bulk_params.merge(:policy_id => @policy.id),
|
|
271
|
+
session: set_session_user(@user)
|
|
272
|
+
|
|
273
|
+
assert_forbidden_missing_permission('assign_policies')
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
test "should forbid change openscap proxy without edit_hosts permission" do
|
|
277
|
+
setup_user('assign', 'policies', nil, @user)
|
|
278
|
+
|
|
279
|
+
put :change_openscap_proxy,
|
|
280
|
+
params: valid_bulk_params.merge(:openscap_proxy_id => @proxy.id),
|
|
281
|
+
session: set_session_user(@user)
|
|
282
|
+
|
|
283
|
+
assert_forbidden_missing_permission('edit_hosts')
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
test "should forbid assign compliance policy when user cannot edit hosts" do
|
|
287
|
+
setup_user('assign', 'policies', nil, @user)
|
|
288
|
+
|
|
289
|
+
put :assign_compliance_policy,
|
|
290
|
+
params: valid_bulk_params.merge(:policy_id => @policy.id),
|
|
291
|
+
session: set_session_user(@user)
|
|
292
|
+
|
|
293
|
+
assert_response :forbidden
|
|
294
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
295
|
+
assert_match(/No hosts matched search, or action unauthorized for selected hosts/, response['error']['message'])
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
private
|
|
300
|
+
|
|
301
|
+
def assert_forbidden_missing_permission(permission)
|
|
302
|
+
assert_response :forbidden
|
|
303
|
+
response = ActiveSupport::JSON.decode(@response.body)
|
|
304
|
+
assert_includes response['error']['missing_permissions'], permission
|
|
305
|
+
end
|
|
132
306
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { MenuItem } from '@patternfly/react-core';
|
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
|
+
import { openBulkModal } from 'foremanReact/common/BulkModalStateHelper';
|
|
5
|
+
import { usePermissions } from 'foremanReact/common/hooks/Permissions/permissionHooks';
|
|
6
|
+
import { EDIT_HOSTS } from 'foremanReact/permissions';
|
|
7
|
+
import { ForemanHostsIndexActionsBarContext } from 'foremanReact/components/HostsIndex';
|
|
8
|
+
import {
|
|
9
|
+
ASSIGN_POLICY_MODAL_ID,
|
|
10
|
+
UNASSIGN_POLICY_MODAL_ID,
|
|
11
|
+
ASSIGN_POLICIES,
|
|
12
|
+
} from './constants';
|
|
13
|
+
|
|
14
|
+
const BulkCompliancePolicyMenuItems = () => {
|
|
15
|
+
const { selectedCount } = useContext(ForemanHostsIndexActionsBarContext);
|
|
16
|
+
|
|
17
|
+
const hasPermission = usePermissions([ASSIGN_POLICIES, EDIT_HOSTS]);
|
|
18
|
+
|
|
19
|
+
const handleAssignClick = () => openBulkModal(ASSIGN_POLICY_MODAL_ID, true);
|
|
20
|
+
const handleUnassignClick = () =>
|
|
21
|
+
openBulkModal(UNASSIGN_POLICY_MODAL_ID, true);
|
|
22
|
+
|
|
23
|
+
if (!hasPermission) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
<MenuItem
|
|
30
|
+
itemId="assign-compliance-policy-dropdown-item"
|
|
31
|
+
key="assign-compliance-policy-dropdown-item"
|
|
32
|
+
onClick={handleAssignClick}
|
|
33
|
+
isDisabled={selectedCount === 0}
|
|
34
|
+
>
|
|
35
|
+
{__('Assign compliance policy')}
|
|
36
|
+
</MenuItem>
|
|
37
|
+
<MenuItem
|
|
38
|
+
itemId="unassign-compliance-policy-dropdown-item"
|
|
39
|
+
key="unassign-compliance-policy-dropdown-item"
|
|
40
|
+
onClick={handleUnassignClick}
|
|
41
|
+
isDisabled={selectedCount === 0}
|
|
42
|
+
>
|
|
43
|
+
{__('Unassign compliance policy')}
|
|
44
|
+
</MenuItem>
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default BulkCompliancePolicyMenuItems;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { FormattedMessage } from 'react-intl';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
+
import {
|
|
6
|
+
Modal,
|
|
7
|
+
Button,
|
|
8
|
+
TextContent,
|
|
9
|
+
Text,
|
|
10
|
+
Select,
|
|
11
|
+
SelectOption,
|
|
12
|
+
SelectList,
|
|
13
|
+
MenuToggle,
|
|
14
|
+
Spinner,
|
|
15
|
+
EmptyState,
|
|
16
|
+
EmptyStateHeader,
|
|
17
|
+
EmptyStateIcon,
|
|
18
|
+
EmptyStateBody,
|
|
19
|
+
Flex,
|
|
20
|
+
FlexItem,
|
|
21
|
+
} from '@patternfly/react-core';
|
|
22
|
+
import { ExclamationCircleIcon, SearchIcon } from '@patternfly/react-icons';
|
|
23
|
+
import { STATUS } from 'foremanReact/constants';
|
|
24
|
+
import { addToast } from 'foremanReact/components/ToastsList/slice';
|
|
25
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
26
|
+
import {
|
|
27
|
+
selectAPIStatus,
|
|
28
|
+
selectAPIResponse,
|
|
29
|
+
} from 'foremanReact/redux/API/APISelectors';
|
|
30
|
+
import {
|
|
31
|
+
buildBulkRequestBody,
|
|
32
|
+
bulkErrorToastParams,
|
|
33
|
+
} from 'foremanReact/components/HostsIndex/BulkActions/helpers';
|
|
34
|
+
import { fetchPolicies, bulkAssignPolicy, bulkUnassignPolicy } from './actions';
|
|
35
|
+
import {
|
|
36
|
+
POLICIES_KEY,
|
|
37
|
+
BULK_ASSIGN_POLICY_KEY,
|
|
38
|
+
BULK_UNASSIGN_POLICY_KEY,
|
|
39
|
+
} from './constants';
|
|
40
|
+
|
|
41
|
+
const BulkCompliancePolicyModal = ({
|
|
42
|
+
isOpen,
|
|
43
|
+
closeModal,
|
|
44
|
+
mode,
|
|
45
|
+
selectAllHostsMode,
|
|
46
|
+
selectedCount,
|
|
47
|
+
fetchBulkParams,
|
|
48
|
+
organizationId,
|
|
49
|
+
locationId,
|
|
50
|
+
onSuccess: onSuccessCallback,
|
|
51
|
+
}) => {
|
|
52
|
+
const dispatch = useDispatch();
|
|
53
|
+
const [policyId, setPolicyId] = useState('');
|
|
54
|
+
const [policySelectOpen, setPolicySelectOpen] = useState(false);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (isOpen) {
|
|
58
|
+
dispatch(fetchPolicies());
|
|
59
|
+
}
|
|
60
|
+
}, [dispatch, isOpen]);
|
|
61
|
+
|
|
62
|
+
const isAssignMode = mode === 'assign';
|
|
63
|
+
const actionKey = isAssignMode
|
|
64
|
+
? BULK_ASSIGN_POLICY_KEY
|
|
65
|
+
: BULK_UNASSIGN_POLICY_KEY;
|
|
66
|
+
|
|
67
|
+
const policies = useSelector(state => selectAPIResponse(state, POLICIES_KEY));
|
|
68
|
+
const policiesStatus = useSelector(state =>
|
|
69
|
+
selectAPIStatus(state, POLICIES_KEY)
|
|
70
|
+
);
|
|
71
|
+
const policiesError = useSelector(
|
|
72
|
+
state => state.API?.[POLICIES_KEY]?.error?.message
|
|
73
|
+
);
|
|
74
|
+
const hostUpdateStatus = useSelector(state =>
|
|
75
|
+
selectAPIStatus(state, actionKey)
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const onToggleClick = () => {
|
|
79
|
+
setPolicySelectOpen(!policySelectOpen);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handlePolicySelect = (event, selection) => {
|
|
83
|
+
setPolicyId(selection);
|
|
84
|
+
setPolicySelectOpen(false);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const getPolicyLabel = id =>
|
|
88
|
+
policies?.results?.find(p => p.id.toString() === id)?.name ||
|
|
89
|
+
__('Select a policy');
|
|
90
|
+
|
|
91
|
+
const toggle = toggleRef => (
|
|
92
|
+
<MenuToggle
|
|
93
|
+
ref={toggleRef}
|
|
94
|
+
ouiaId="bulk-compliance-policy-toggle"
|
|
95
|
+
onClick={onToggleClick}
|
|
96
|
+
isExpanded={policySelectOpen}
|
|
97
|
+
style={{ width: '100%' }}
|
|
98
|
+
>
|
|
99
|
+
{policyId ? getPolicyLabel(policyId) : __('Select a policy')}
|
|
100
|
+
</MenuToggle>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const handleModalClose = () => {
|
|
104
|
+
setPolicyId('');
|
|
105
|
+
setPolicySelectOpen(false);
|
|
106
|
+
closeModal();
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleError = error => {
|
|
110
|
+
handleModalClose();
|
|
111
|
+
dispatch(addToast(bulkErrorToastParams(error, actionKey)));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleSuccess = response => {
|
|
115
|
+
dispatch(
|
|
116
|
+
addToast({
|
|
117
|
+
type: 'success',
|
|
118
|
+
message: response.data.message,
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
if (onSuccessCallback) onSuccessCallback();
|
|
122
|
+
handleModalClose();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleConfirm = () => {
|
|
126
|
+
const requestBody = buildBulkRequestBody({
|
|
127
|
+
fetchBulkParams,
|
|
128
|
+
organizationId,
|
|
129
|
+
locationId,
|
|
130
|
+
policy_id: policyId,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (mode === 'assign') {
|
|
134
|
+
dispatch(bulkAssignPolicy(requestBody, handleSuccess, handleError));
|
|
135
|
+
} else {
|
|
136
|
+
dispatch(bulkUnassignPolicy(requestBody, handleSuccess, handleError));
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const title = isAssignMode
|
|
141
|
+
? __('Assign Compliance Policy')
|
|
142
|
+
: __('Unassign Compliance Policy');
|
|
143
|
+
const confirmButtonText = isAssignMode
|
|
144
|
+
? __('Assign policy')
|
|
145
|
+
: __('Unassign policy');
|
|
146
|
+
|
|
147
|
+
const modalActions = [
|
|
148
|
+
<Button
|
|
149
|
+
key="confirm"
|
|
150
|
+
ouiaId="bulk-compliance-policy-modal-confirm-button"
|
|
151
|
+
variant="primary"
|
|
152
|
+
onClick={handleConfirm}
|
|
153
|
+
isDisabled={hostUpdateStatus === STATUS.PENDING || policyId === ''}
|
|
154
|
+
isLoading={hostUpdateStatus === STATUS.PENDING}
|
|
155
|
+
>
|
|
156
|
+
{confirmButtonText}
|
|
157
|
+
</Button>,
|
|
158
|
+
<Button
|
|
159
|
+
key="cancel"
|
|
160
|
+
ouiaId="bulk-compliance-policy-modal-cancel-button"
|
|
161
|
+
variant="link"
|
|
162
|
+
onClick={handleModalClose}
|
|
163
|
+
>
|
|
164
|
+
{__('Cancel')}
|
|
165
|
+
</Button>,
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const warningMessage = isAssignMode
|
|
169
|
+
? __(
|
|
170
|
+
'The selected policy will be assigned to the selected hosts. Some hosts may already be assigned to this policy.'
|
|
171
|
+
)
|
|
172
|
+
: __(
|
|
173
|
+
'The selected policy will be unassigned from the selected hosts. Some hosts may not be assigned to this policy.'
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Modal
|
|
178
|
+
isOpen={isOpen}
|
|
179
|
+
onClose={handleModalClose}
|
|
180
|
+
onEscapePress={handleModalClose}
|
|
181
|
+
title={title}
|
|
182
|
+
variant="medium"
|
|
183
|
+
position="top"
|
|
184
|
+
actions={modalActions}
|
|
185
|
+
id="bulk-compliance-policy-modal"
|
|
186
|
+
key="bulk-compliance-policy-modal"
|
|
187
|
+
ouiaId="bulk-compliance-policy-modal"
|
|
188
|
+
>
|
|
189
|
+
<TextContent>
|
|
190
|
+
<Text ouiaId="bulk-compliance-policy-count">
|
|
191
|
+
{selectAllHostsMode ? (
|
|
192
|
+
<FormattedMessage
|
|
193
|
+
id="bulk-compliance-policy-all-hosts"
|
|
194
|
+
defaultMessage="{action} will affect {boldCount} selected hosts."
|
|
195
|
+
values={{
|
|
196
|
+
action: isAssignMode ? __('Assigning') : __('Unassigning'),
|
|
197
|
+
boldCount: <strong>{__('All')}</strong>,
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
) : (
|
|
201
|
+
<FormattedMessage
|
|
202
|
+
id="bulk-compliance-policy-selected-hosts"
|
|
203
|
+
defaultMessage="{action} will affect {boldCount} selected {count, plural, one {host} other {hosts}}."
|
|
204
|
+
values={{
|
|
205
|
+
action: isAssignMode ? __('Assigning') : __('Unassigning'),
|
|
206
|
+
count: selectedCount,
|
|
207
|
+
boldCount: <strong>{selectedCount}</strong>,
|
|
208
|
+
}}
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
</Text>
|
|
212
|
+
<Text ouiaId="bulk-compliance-policy-warning">{warningMessage}</Text>
|
|
213
|
+
</TextContent>
|
|
214
|
+
|
|
215
|
+
{policiesStatus === STATUS.PENDING && (
|
|
216
|
+
<Flex
|
|
217
|
+
direction={{ default: 'column' }}
|
|
218
|
+
alignItems={{ default: 'alignItemsCenter' }}
|
|
219
|
+
spaceItems={{ default: 'spaceItemsSm' }}
|
|
220
|
+
>
|
|
221
|
+
<FlexItem>
|
|
222
|
+
<Spinner size="lg" aria-label={__('Loading policies')} />
|
|
223
|
+
</FlexItem>
|
|
224
|
+
<FlexItem>
|
|
225
|
+
<Text ouiaId="loading-policies-text">
|
|
226
|
+
{__('Loading policies...')}
|
|
227
|
+
</Text>
|
|
228
|
+
</FlexItem>
|
|
229
|
+
</Flex>
|
|
230
|
+
)}
|
|
231
|
+
|
|
232
|
+
{policiesStatus === STATUS.ERROR && (
|
|
233
|
+
<EmptyState>
|
|
234
|
+
<EmptyStateHeader
|
|
235
|
+
titleText={__('Failed to load policies')}
|
|
236
|
+
icon={<EmptyStateIcon icon={ExclamationCircleIcon} />}
|
|
237
|
+
headingLevel="h4"
|
|
238
|
+
/>
|
|
239
|
+
<EmptyStateBody>
|
|
240
|
+
{policiesError || __('An error occurred while fetching policies.')}
|
|
241
|
+
</EmptyStateBody>
|
|
242
|
+
</EmptyState>
|
|
243
|
+
)}
|
|
244
|
+
|
|
245
|
+
{policiesStatus === STATUS.RESOLVED && policies?.results?.length === 0 && (
|
|
246
|
+
<EmptyState>
|
|
247
|
+
<EmptyStateHeader
|
|
248
|
+
titleText={__('No policies available')}
|
|
249
|
+
icon={<EmptyStateIcon icon={SearchIcon} />}
|
|
250
|
+
headingLevel="h4"
|
|
251
|
+
/>
|
|
252
|
+
<EmptyStateBody>
|
|
253
|
+
{__(
|
|
254
|
+
'There are no compliance policies available in your current organization and location.'
|
|
255
|
+
)}
|
|
256
|
+
</EmptyStateBody>
|
|
257
|
+
</EmptyState>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{policiesStatus === STATUS.RESOLVED && policies?.results?.length > 0 && (
|
|
261
|
+
<Select
|
|
262
|
+
id="compliance-policy-select"
|
|
263
|
+
isOpen={policySelectOpen}
|
|
264
|
+
selected={policyId}
|
|
265
|
+
onSelect={handlePolicySelect}
|
|
266
|
+
onOpenChange={isSelectOpen => setPolicySelectOpen(isSelectOpen)}
|
|
267
|
+
toggle={toggle}
|
|
268
|
+
shouldFocusToggleOnSelect
|
|
269
|
+
ouiaId="bulk-compliance-policy-select"
|
|
270
|
+
>
|
|
271
|
+
<SelectList>
|
|
272
|
+
{policies.results.map(policy => (
|
|
273
|
+
<SelectOption key={policy.id} value={policy.id.toString()}>
|
|
274
|
+
{policy.name}
|
|
275
|
+
</SelectOption>
|
|
276
|
+
))}
|
|
277
|
+
</SelectList>
|
|
278
|
+
</Select>
|
|
279
|
+
)}
|
|
280
|
+
</Modal>
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
BulkCompliancePolicyModal.propTypes = {
|
|
285
|
+
isOpen: PropTypes.bool,
|
|
286
|
+
closeModal: PropTypes.func,
|
|
287
|
+
mode: PropTypes.oneOf(['assign', 'unassign']).isRequired,
|
|
288
|
+
fetchBulkParams: PropTypes.func.isRequired,
|
|
289
|
+
selectedCount: PropTypes.number.isRequired,
|
|
290
|
+
selectAllHostsMode: PropTypes.bool.isRequired,
|
|
291
|
+
organizationId: PropTypes.number,
|
|
292
|
+
locationId: PropTypes.number,
|
|
293
|
+
onSuccess: PropTypes.func,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
BulkCompliancePolicyModal.defaultProps = {
|
|
297
|
+
isOpen: false,
|
|
298
|
+
closeModal: () => {},
|
|
299
|
+
organizationId: undefined,
|
|
300
|
+
locationId: undefined,
|
|
301
|
+
onSuccess: undefined,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export default BulkCompliancePolicyModal;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
|
+
import { ForemanActionsBarContext } from 'foremanReact/components/HostDetails/ActionsBar';
|
|
3
|
+
import { useBulkModalOpen } from 'foremanReact/common/BulkModalStateHelper';
|
|
4
|
+
import BulkCompliancePolicyModal from './BulkCompliancePolicyModal';
|
|
5
|
+
import { ASSIGN_POLICY_MODAL_ID, UNASSIGN_POLICY_MODAL_ID } from './constants';
|
|
6
|
+
|
|
7
|
+
export const BulkAssignPolicyModalScene = () => {
|
|
8
|
+
const {
|
|
9
|
+
selectAllHostsMode,
|
|
10
|
+
selectedCount,
|
|
11
|
+
fetchBulkParams,
|
|
12
|
+
organizationId,
|
|
13
|
+
locationId,
|
|
14
|
+
refreshTableData,
|
|
15
|
+
} = useContext(ForemanActionsBarContext) || {};
|
|
16
|
+
|
|
17
|
+
const { isOpen, close: closeModal } = useBulkModalOpen(
|
|
18
|
+
ASSIGN_POLICY_MODAL_ID
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<BulkCompliancePolicyModal
|
|
23
|
+
key="bulk-assign-compliance-policy-modal"
|
|
24
|
+
selectAllHostsMode={selectAllHostsMode}
|
|
25
|
+
selectedCount={selectedCount}
|
|
26
|
+
fetchBulkParams={fetchBulkParams}
|
|
27
|
+
organizationId={organizationId}
|
|
28
|
+
locationId={locationId}
|
|
29
|
+
isOpen={isOpen}
|
|
30
|
+
closeModal={closeModal}
|
|
31
|
+
mode="assign"
|
|
32
|
+
onSuccess={refreshTableData}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const BulkUnassignPolicyModalScene = () => {
|
|
38
|
+
const {
|
|
39
|
+
selectAllHostsMode,
|
|
40
|
+
selectedCount,
|
|
41
|
+
fetchBulkParams,
|
|
42
|
+
organizationId,
|
|
43
|
+
locationId,
|
|
44
|
+
refreshTableData,
|
|
45
|
+
} = useContext(ForemanActionsBarContext) || {};
|
|
46
|
+
|
|
47
|
+
const { isOpen, close: closeModal } = useBulkModalOpen(
|
|
48
|
+
UNASSIGN_POLICY_MODAL_ID
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<BulkCompliancePolicyModal
|
|
53
|
+
key="bulk-unassign-compliance-policy-modal"
|
|
54
|
+
selectAllHostsMode={selectAllHostsMode}
|
|
55
|
+
selectedCount={selectedCount}
|
|
56
|
+
fetchBulkParams={fetchBulkParams}
|
|
57
|
+
organizationId={organizationId}
|
|
58
|
+
locationId={locationId}
|
|
59
|
+
isOpen={isOpen}
|
|
60
|
+
closeModal={closeModal}
|
|
61
|
+
mode="unassign"
|
|
62
|
+
onSuccess={refreshTableData}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
fetchPolicies,
|
|
5
|
+
bulkAssignPolicy,
|
|
6
|
+
bulkUnassignPolicy,
|
|
7
|
+
} from '../actions';
|
|
8
|
+
import {
|
|
9
|
+
POLICIES_KEY,
|
|
10
|
+
BULK_ASSIGN_POLICY_KEY,
|
|
11
|
+
BULK_UNASSIGN_POLICY_KEY,
|
|
12
|
+
} from '../constants';
|
|
13
|
+
|
|
14
|
+
jest.mock('foremanReact/redux/API', () => ({
|
|
15
|
+
APIActions: {
|
|
16
|
+
get: jest.fn(params => ({
|
|
17
|
+
type: 'API_GET',
|
|
18
|
+
params,
|
|
19
|
+
})),
|
|
20
|
+
put: jest.fn(params => ({
|
|
21
|
+
type: 'API_PUT',
|
|
22
|
+
params,
|
|
23
|
+
})),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
jest.mock('foremanReact/common/helpers', () => ({
|
|
28
|
+
foremanUrl: jest.fn(path => path),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
jest.mock('foremanReact/common/I18n', () => ({
|
|
32
|
+
translate: jest.fn(text => text),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe('Compliance Policy Actions', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
jest.clearAllMocks();
|
|
38
|
+
APIActions.put.mockReturnValue({ type: 'API_PUT' });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('fetchPolicies', () => {
|
|
42
|
+
it('dispatches GET request to compliance policies API', () => {
|
|
43
|
+
const action = fetchPolicies();
|
|
44
|
+
|
|
45
|
+
expect(APIActions.get).toHaveBeenCalledWith({
|
|
46
|
+
url: '/api/v2/compliance/policies',
|
|
47
|
+
key: POLICIES_KEY,
|
|
48
|
+
params: { per_page: 'all' },
|
|
49
|
+
});
|
|
50
|
+
expect(action.type).toBe('API_GET');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('bulkAssignPolicy', () => {
|
|
55
|
+
const requestBody = {
|
|
56
|
+
included: {
|
|
57
|
+
search: 'id ^ (1,2,3)',
|
|
58
|
+
},
|
|
59
|
+
policy_id: '5',
|
|
60
|
+
organization_id: 1,
|
|
61
|
+
location_id: 2,
|
|
62
|
+
};
|
|
63
|
+
const handleSuccess = jest.fn();
|
|
64
|
+
const handleError = jest.fn();
|
|
65
|
+
|
|
66
|
+
it('dispatches PUT request to assign policy endpoint', () => {
|
|
67
|
+
bulkAssignPolicy(requestBody, handleSuccess, handleError);
|
|
68
|
+
|
|
69
|
+
expect(APIActions.put).toHaveBeenCalledTimes(1);
|
|
70
|
+
const callArgs = APIActions.put.mock.calls[0][0];
|
|
71
|
+
|
|
72
|
+
expect(callArgs.key).toBe(BULK_ASSIGN_POLICY_KEY);
|
|
73
|
+
expect(callArgs.url).toContain(
|
|
74
|
+
'/api/v2/compliance/hosts/bulk/assign_compliance_policy'
|
|
75
|
+
);
|
|
76
|
+
expect(callArgs.params).toEqual(requestBody);
|
|
77
|
+
expect(callArgs.handleSuccess).toBe(handleSuccess);
|
|
78
|
+
expect(callArgs.handleError).toBe(handleError);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('passes error handler callback', () => {
|
|
82
|
+
bulkAssignPolicy(requestBody, handleSuccess, handleError);
|
|
83
|
+
const callArgs = APIActions.put.mock.calls[0][0];
|
|
84
|
+
expect(callArgs.handleError).toBe(handleError);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('bulkUnassignPolicy', () => {
|
|
89
|
+
const requestBody = {
|
|
90
|
+
included: {
|
|
91
|
+
search: 'name ~ test',
|
|
92
|
+
},
|
|
93
|
+
policy_id: '3',
|
|
94
|
+
organization_id: 1,
|
|
95
|
+
location_id: 2,
|
|
96
|
+
};
|
|
97
|
+
const handleSuccess = jest.fn();
|
|
98
|
+
const handleError = jest.fn();
|
|
99
|
+
|
|
100
|
+
it('dispatches PUT request to unassign policy endpoint', () => {
|
|
101
|
+
bulkUnassignPolicy(requestBody, handleSuccess, handleError);
|
|
102
|
+
|
|
103
|
+
expect(APIActions.put).toHaveBeenCalledTimes(1);
|
|
104
|
+
const callArgs = APIActions.put.mock.calls[0][0];
|
|
105
|
+
|
|
106
|
+
expect(callArgs.key).toBe(BULK_UNASSIGN_POLICY_KEY);
|
|
107
|
+
expect(callArgs.url).toContain(
|
|
108
|
+
'/api/v2/compliance/hosts/bulk/unassign_compliance_policy'
|
|
109
|
+
);
|
|
110
|
+
expect(callArgs.params).toEqual(requestBody);
|
|
111
|
+
expect(callArgs.handleSuccess).toBe(handleSuccess);
|
|
112
|
+
expect(callArgs.handleError).toBe(handleError);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('passes error handler callback', () => {
|
|
116
|
+
bulkUnassignPolicy(requestBody, handleSuccess, handleError);
|
|
117
|
+
const callArgs = APIActions.put.mock.calls[0][0];
|
|
118
|
+
expect(callArgs.handleError).toBe(handleError);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { APIActions } from 'foremanReact/redux/API';
|
|
2
|
+
import { foremanUrl } from 'foremanReact/common/helpers';
|
|
3
|
+
import {
|
|
4
|
+
POLICIES_KEY,
|
|
5
|
+
BULK_ASSIGN_POLICY_KEY,
|
|
6
|
+
BULK_UNASSIGN_POLICY_KEY,
|
|
7
|
+
} from './constants';
|
|
8
|
+
|
|
9
|
+
export const fetchPolicies = () =>
|
|
10
|
+
APIActions.get({
|
|
11
|
+
url: foremanUrl('/api/v2/compliance/policies'),
|
|
12
|
+
key: POLICIES_KEY,
|
|
13
|
+
params: { per_page: 'all' },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const bulkAssignPolicy = (params, handleSuccess, handleError) =>
|
|
17
|
+
APIActions.put({
|
|
18
|
+
url: foremanUrl('/api/v2/compliance/hosts/bulk/assign_compliance_policy'),
|
|
19
|
+
key: BULK_ASSIGN_POLICY_KEY,
|
|
20
|
+
params,
|
|
21
|
+
handleSuccess,
|
|
22
|
+
handleError,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export const bulkUnassignPolicy = (params, handleSuccess, handleError) =>
|
|
26
|
+
APIActions.put({
|
|
27
|
+
url: foremanUrl('/api/v2/compliance/hosts/bulk/unassign_compliance_policy'),
|
|
28
|
+
key: BULK_UNASSIGN_POLICY_KEY,
|
|
29
|
+
params,
|
|
30
|
+
handleSuccess,
|
|
31
|
+
handleError,
|
|
32
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const POLICIES_KEY = 'COMPLIANCE_POLICIES';
|
|
2
|
+
export const BULK_ASSIGN_POLICY_KEY = 'BULK_ASSIGN_COMPLIANCE_POLICY';
|
|
3
|
+
export const BULK_UNASSIGN_POLICY_KEY = 'BULK_UNASSIGN_COMPLIANCE_POLICY';
|
|
4
|
+
|
|
5
|
+
export const ASSIGN_POLICY_MODAL_ID = 'assign-compliance-policy-modal';
|
|
6
|
+
export const UNASSIGN_POLICY_MODAL_ID = 'unassign-compliance-policy-modal';
|
|
7
|
+
|
|
8
|
+
export const ASSIGN_POLICIES = 'assign_policies';
|
data/webpack/global_index.js
CHANGED
|
@@ -4,10 +4,15 @@ import HostKebabItems from './components/HostExtentions/HostKebabItems';
|
|
|
4
4
|
import BulkChangeOpenscapProxyModalScene, {
|
|
5
5
|
ChangeOpenscapProxyMenuItem,
|
|
6
6
|
} from './components/HostsIndex/ChangeOpenscapProxyAction';
|
|
7
|
+
import BulkCompliancePolicyMenuItems from './components/BulkActions/compliancePolicy/BulkCompliancePolicyMenuItems';
|
|
8
|
+
import {
|
|
9
|
+
BulkAssignPolicyModalScene,
|
|
10
|
+
BulkUnassignPolicyModalScene,
|
|
11
|
+
} from './components/BulkActions/compliancePolicy/BulkCompliancePolicyModalScene';
|
|
7
12
|
|
|
8
13
|
const HOST_ASSOCIATIONS_WEIGHT = 1212;
|
|
9
14
|
const BULK_MODAL_WEIGHT = 100;
|
|
10
|
-
|
|
15
|
+
const BULK_POLICY_MODAL_WEIGHT = 200;
|
|
11
16
|
const OPENSCAP_KEBAB_WEIGHT = 400;
|
|
12
17
|
|
|
13
18
|
addGlobalFill(
|
|
@@ -30,3 +35,24 @@ addGlobalFill(
|
|
|
30
35
|
<BulkChangeOpenscapProxyModalScene key="bulk-change-openscap-proxy-modal" />,
|
|
31
36
|
BULK_MODAL_WEIGHT
|
|
32
37
|
);
|
|
38
|
+
|
|
39
|
+
addGlobalFill(
|
|
40
|
+
'_host-associations',
|
|
41
|
+
'compliance-policy-bulk-actions',
|
|
42
|
+
<BulkCompliancePolicyMenuItems key="compliance-policy-menu-items" />,
|
|
43
|
+
BULK_POLICY_MODAL_WEIGHT
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
addGlobalFill(
|
|
47
|
+
'_all-hosts-modals',
|
|
48
|
+
'assign-compliance-policy-modal',
|
|
49
|
+
<BulkAssignPolicyModalScene key="bulk-assign-policy-modal" />,
|
|
50
|
+
BULK_POLICY_MODAL_WEIGHT
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
addGlobalFill(
|
|
54
|
+
'_all-hosts-modals',
|
|
55
|
+
'unassign-compliance-policy-modal',
|
|
56
|
+
<BulkUnassignPolicyModalScene key="bulk-unassign-policy-modal" />,
|
|
57
|
+
BULK_POLICY_MODAL_WEIGHT
|
|
58
|
+
);
|
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: 13.
|
|
4
|
+
version: 13.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- slukasik@redhat.com
|
|
@@ -366,6 +366,12 @@ files:
|
|
|
366
366
|
- test/unit/services/lookup_key_overrider_test.rb
|
|
367
367
|
- test/unit/services/report_dashboard/data_test.rb
|
|
368
368
|
- test/unit/tailoring_file_test.rb
|
|
369
|
+
- webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyMenuItems.js
|
|
370
|
+
- webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyModal.js
|
|
371
|
+
- webpack/components/BulkActions/compliancePolicy/BulkCompliancePolicyModalScene.js
|
|
372
|
+
- webpack/components/BulkActions/compliancePolicy/__tests__/actions.test.js
|
|
373
|
+
- webpack/components/BulkActions/compliancePolicy/actions.js
|
|
374
|
+
- webpack/components/BulkActions/compliancePolicy/constants.js
|
|
369
375
|
- webpack/components/EmptyState.js
|
|
370
376
|
- webpack/components/HostExtentions/HostKebabItems.js
|
|
371
377
|
- webpack/components/HostsIndex/BulkActions/changeOpenscapProxy/BulkChangeOpenscapProxyModal.js
|