foreman_rh_cloud 14.1.3 → 14.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/services/foreman_rh_cloud/insights_api_forwarder.rb +157 -5
  3. data/lib/foreman_rh_cloud/plugin.rb +16 -5
  4. data/lib/foreman_rh_cloud/version.rb +1 -1
  5. data/package.json +1 -1
  6. data/test/unit/services/foreman_rh_cloud/insights_api_forwarder_test.rb +240 -4
  7. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountList.fixtures.js +0 -6
  8. data/webpack/ForemanInventoryUpload/Components/AccountList/AccountListReducer.js +0 -2
  9. data/webpack/ForemanInventoryUpload/Components/AccountList/__tests__/AccountListReducer.test.js +0 -2
  10. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.js +0 -2
  11. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageHeader.scss +1 -2
  12. data/webpack/ForemanInventoryUpload/Components/PageHeader/PageTitle.js +13 -7
  13. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageHeader.test.js +0 -5
  14. data/webpack/ForemanInventoryUpload/Components/PageHeader/__tests__/PageTitle.test.js +34 -1
  15. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/ToolbarButtons.js +0 -4
  16. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/ToolbarButtons/__tests__/ToolbarButtons.test.js +18 -47
  17. data/webpack/ForemanInventoryUpload/ForemanInventoryHelpers.js +4 -4
  18. data/webpack/ForemanInventoryUpload/__tests__/ForemanInventoryHelpers.test.js +16 -1
  19. metadata +2 -11
  20. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorActions.js +0 -21
  21. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorButton.js +0 -59
  22. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorConstants.js +0 -6
  23. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/CloudConnectorSelectors.js +0 -19
  24. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/__tests__/CloudConnectorButton.test.js +0 -51
  25. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudConnectorButton/index.js +0 -34
  26. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.js +0 -66
  27. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/SettingsWarning.test.js +0 -61
  28. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SettingsWarning/index.js +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89c6b782cc34fb3ffc4f13ad66c435c5729028df995e7bdba1c15e668a72fe89
4
- data.tar.gz: 356fb55567950b341b4509f6ee39e30b24127c695b3924765070167800595f44
3
+ metadata.gz: 91157d5050744236651134413110454e4d595e1e25644eb1676d58e4990476d1
4
+ data.tar.gz: acb8689d93c1d2aedf280049ff18075ad7989693ef75fea5a8b1babef63b447c
5
5
  SHA512:
6
- metadata.gz: 8bdb9ff78ed0950bd67f5bcc1483a0f2568af9a3462633ef6fc25d061bf942712d416d717f43e3dcef448dddb11cc6f7b9ad1fe62d6530ad2dbf44af3a3eb4a6
7
- data.tar.gz: f55334cb175a624ae8aeb21242b609ce8af3c49061c7e09b460b46fef3d3239cab42232c0c52eb043ed345a661b331409545344f24e89e46e3fb9a782666e1bc
6
+ metadata.gz: 5f2378f058fde9220f4fa8ca17a094dfe6e32f3f33456304c0178ca3b5023abf8b6bfd09fdcbff064e914e063c4fc6a9b66536d6703e259a4f28569202c37813
7
+ data.tar.gz: 1d0adbd1839a3e73eada13ca910522407af5a66e838934ad327f8c36cb41058f5693c7d5d646caa3b7b1fba71670e184f24df1ee90b122d4fdd7f05338a845de
@@ -8,6 +8,30 @@ module ForemanRhCloud
8
8
  #
9
9
  # Foreman Permission | Paths
10
10
  # ---------------------|--------------------------------------------------
11
+ # view_compliance | GET /api/inventory/v1/hosts(/*)
12
+ # view_compliance | GET /api/compliance/v2/systems
13
+ # view_compliance | GET /api/compliance/v2/systems/{system_id}
14
+ # view_compliance | GET /api/compliance/v2/systems/os_versions
15
+ # view_compliance | GET /api/compliance/v2/systems/{system_id}/policies
16
+ # view_compliance | GET /api/compliance/v2/systems/{system_id}/reports
17
+ # view_compliance | GET /api/compliance/v2/reports/{report_id}/systems
18
+ # view_compliance | GET /api/compliance/v2/reports/{report_id}/systems/{system_id}
19
+ # view_compliance | GET /api/compliance/v2/reports/{report_id}/systems/os_versions
20
+ # view_compliance | GET /api/compliance/v2/policies/{policy_id}/systems/os_versions
21
+ # view_compliance | GET /api/compliance/v2/*
22
+ # edit_compliance | POST /api/compliance/v2/policies
23
+ # edit_compliance | DELETE /api/compliance/v2/policies/{policy_id}
24
+ # edit_compliance | PATCH /api/compliance/v2/policies/{policy_id}
25
+ # edit_compliance | POST /api/compliance/v2/policies/{policy_id}/systems
26
+ # edit_compliance | DELETE /api/compliance/v2/policies/{policy_id}/systems/{system_id}
27
+ # edit_compliance | PATCH /api/compliance/v2/policies/{policy_id}/systems/{system_id}
28
+ # edit_compliance | POST /api/compliance/v2/policies/{policy_id}/tailorings
29
+ # edit_compliance | PATCH /api/compliance/v2/policies/{policy_id}/tailorings/{tailoring_id}
30
+ # edit_compliance | POST /api/compliance/v2/policies/{policy_id}/tailorings/{tailoring_id}/rules
31
+ # edit_compliance | DELETE /api/compliance/v2/policies/{policy_id}/tailorings/{tailoring_id}/rules/{rule_id}
32
+ # edit_compliance | PATCH /api/compliance/v2/policies/{policy_id}/tailorings/{tailoring_id}/rules/{rule_id}
33
+ # edit_compliance | DELETE /api/compliance/v2/reports/{report_id}
34
+ #
11
35
  # view_vulnerability | GET /api/inventory/v1/hosts(/*)
12
36
  # view_vulnerability | GET /api/vulnerability/v1/*
13
37
  # | POST /api/vulnerability/v1/vulnerabilities/cves
@@ -24,12 +48,140 @@ module ForemanRhCloud
24
48
  # | POST /api/insights/v1/rule/{rule_id}/unack_hosts/
25
49
  #
26
50
  SCOPED_REQUESTS = [
27
- # Inventory hosts - requires view_vulnerability for GET
51
+ # Inventory hosts - shared dependency;
52
+ # - requires view_compliance or view_vulnerability for GET
28
53
  {
29
54
  test: %r{api/inventory/v1/hosts(/.*)?$},
30
55
  tag_name: :tags,
31
56
  permissions: {
32
- 'GET' => :view_vulnerability,
57
+ 'GET' => [:view_compliance, :view_vulnerability],
58
+ },
59
+ },
60
+ # Policies - requires view_compliance for GET, edit_compliance for POST/DELETE/PATCH
61
+ {
62
+ test: %r{api/compliance/v2/policies(/[^/]*)?$},
63
+ permissions: {
64
+ 'GET' => :view_compliance,
65
+ 'POST' => :edit_compliance,
66
+ 'DELETE' => :edit_compliance,
67
+ 'PATCH' => :edit_compliance,
68
+ },
69
+ },
70
+ # System and policies assigned to them - requires view_compliance for GET, edit_compliance for POST/DELETE/PATCH
71
+ {
72
+ test: %r{api/compliance/v2/policies/[^/]+/systems(/[^/]*)?$},
73
+ tag_name: :tags,
74
+ permissions: {
75
+ 'GET' => :view_compliance,
76
+ 'POST' => :edit_compliance,
77
+ 'DELETE' => :edit_compliance,
78
+ 'PATCH' => :edit_compliance,
79
+ },
80
+ },
81
+ # Tailorings of assigned policies - requires view_compliance for GET, edit_compliance for POST/PATCH
82
+ {
83
+ test: %r{api/compliance/v2/policies/[^/]+/tailorings(/[^/]*)?$},
84
+ permissions: {
85
+ 'GET' => :view_compliance,
86
+ 'POST' => :edit_compliance,
87
+ 'PATCH' => :edit_compliance,
88
+ },
89
+ },
90
+ # Rules of Tailorings - requires view_compliance for GET, edit_compliance for POST/DELETE/PATCH
91
+ {
92
+ test: %r{api/compliance/v2/policies/[^/]+/tailorings/[^/]+/rules(/[^/]*)?$},
93
+ permissions: {
94
+ 'GET' => :view_compliance,
95
+ 'POST' => :edit_compliance,
96
+ 'DELETE' => :edit_compliance,
97
+ 'PATCH' => :edit_compliance,
98
+ },
99
+ },
100
+ # Compliance Reports - requires view_compliance for GET, edit_compliance for DELETE
101
+ {
102
+ test: %r{api/compliance/v2/reports(/[^/]*)?$},
103
+ permissions: {
104
+ 'GET' => :view_compliance,
105
+ 'DELETE' => :edit_compliance,
106
+ },
107
+ },
108
+ # Systems assigned to a report - requires view_compliance for GET
109
+ {
110
+ test: %r{api/compliance/v2/reports/[^/]+/systems$},
111
+ tag_name: :tags,
112
+ permissions: {
113
+ 'GET' => :view_compliance,
114
+ },
115
+ },
116
+ # Individual system in a report - requires view_compliance for GET
117
+ {
118
+ test: %r{api/compliance/v2/reports/[^/]+/systems/[^/]+$},
119
+ tag_name: :tags,
120
+ permissions: {
121
+ 'GET' => :view_compliance,
122
+ },
123
+ },
124
+ # OS versions for systems in a report - requires view_compliance for GET
125
+ {
126
+ test: %r{api/compliance/v2/reports/[^/]+/systems/os_versions$},
127
+ tag_name: :tags,
128
+ permissions: {
129
+ 'GET' => :view_compliance,
130
+ },
131
+ },
132
+ # OS versions for systems in a policy - requires view_compliance for GET
133
+ {
134
+ test: %r{api/compliance/v2/policies/[^/]+/systems/os_versions$},
135
+ tag_name: :tags,
136
+ permissions: {
137
+ 'GET' => :view_compliance,
138
+ },
139
+ },
140
+ # Compliance Systems list - requires view_compliance for GET
141
+ {
142
+ test: %r{api/compliance/v2/systems$},
143
+ tag_name: :tags,
144
+ permissions: {
145
+ 'GET' => :view_compliance,
146
+ },
147
+ },
148
+ # Individual compliance system - requires view_compliance for GET
149
+ {
150
+ test: %r{api/compliance/v2/systems/[^/]+$},
151
+ tag_name: :tags,
152
+ permissions: {
153
+ 'GET' => :view_compliance,
154
+ },
155
+ },
156
+ # OS versions for all systems - requires view_compliance for GET
157
+ {
158
+ test: %r{api/compliance/v2/systems/os_versions$},
159
+ tag_name: :tags,
160
+ permissions: {
161
+ 'GET' => :view_compliance,
162
+ },
163
+ },
164
+ # Policies for a specific system - requires view_compliance for GET
165
+ {
166
+ test: %r{api/compliance/v2/systems/[^/]+/policies$},
167
+ tag_name: :tags,
168
+ permissions: {
169
+ 'GET' => :view_compliance,
170
+ },
171
+ },
172
+ # Reports for a specific system - requires view_compliance for GET
173
+ {
174
+ test: %r{api/compliance/v2/systems/[^/]+/reports$},
175
+ tag_name: :tags,
176
+ permissions: {
177
+ 'GET' => :view_compliance,
178
+ },
179
+ },
180
+ # Other compliance endpoints - GET requires view_compliance
181
+ {
182
+ test: %r{api/compliance/v2/.*},
183
+ permissions: {
184
+ 'GET' => :view_compliance,
33
185
  },
34
186
  },
35
187
  # Vulnerability CVEs list - POST requires view_vulnerability (no tags support per OpenAPI spec)
@@ -136,9 +288,9 @@ module ForemanRhCloud
136
288
 
137
289
  def forward_request(original_request, path, controller_name, user, organization, location)
138
290
  # Check permissions before forwarding
139
- permission = required_permission_for(path, original_request.request_method)
140
- if permission && !user&.can?(permission)
141
- logger.warn("User #{user&.login || 'anonymous'} lacks permission #{permission} for #{original_request.request_method} #{path}")
291
+ permissions = required_permission_for(path, original_request.request_method)
292
+ if permissions && Array(permissions).none? { |p| user&.can?(p) }
293
+ logger.warn("User #{user&.login || 'anonymous'} lacks permissions #{Array(permissions).join(', ')} for #{original_request.request_method} #{path}")
142
294
  raise ::Foreman::PermissionMissingException.new(N_("You do not have permission to perform this action"))
143
295
  end
144
296
 
@@ -71,6 +71,17 @@ module ForemanRhCloud
71
71
  :control_organization_insights,
72
72
  'insights_cloud/settings': [:set_org_parameter]
73
73
  )
74
+ # Insights Compliance permissions
75
+ permission(
76
+ :view_compliance,
77
+ {},
78
+ :resource_type => 'ForemanRhCloud'
79
+ )
80
+ permission(
81
+ :edit_compliance,
82
+ {},
83
+ :resource_type => 'ForemanRhCloud'
84
+ )
74
85
  # Insights Vulnerability permissions
75
86
  permission(
76
87
  :view_vulnerability,
@@ -98,19 +109,19 @@ module ForemanRhCloud
98
109
  # Core RH Cloud permissions for inventory upload and sync
99
110
  rh_cloud_permissions = [:view_foreman_rh_cloud, :generate_foreman_rh_cloud, :view_insights_hits, :dispatch_cloud_requests, :control_organization_insights]
100
111
 
101
- # Insights application permissions (Vulnerability, Advisor)
102
- insights_permissions = [:view_vulnerability, :edit_vulnerability, :view_advisor, :edit_advisor]
112
+ # Insights application permissions (Compliance, Vulnerability, Advisor)
113
+ insights_permissions = [:view_compliance, :edit_compliance, :view_vulnerability, :edit_vulnerability, :view_advisor, :edit_advisor]
103
114
 
104
115
  plugin_permissions = rh_cloud_permissions + insights_permissions
105
116
 
106
- read_only_permissions = [:view_foreman_rh_cloud, :view_insights_hits, :view_vulnerability, :view_advisor]
117
+ read_only_permissions = [:view_foreman_rh_cloud, :view_insights_hits, :view_compliance, :view_vulnerability, :view_advisor]
107
118
 
108
119
  role 'ForemanRhCloud', plugin_permissions, 'Role granting permissions to view the hosts inventory,
109
120
  generate a report, upload it to the cloud, download it locally,
110
- and manage Insights Vulnerability and Advisor features'
121
+ and manage Insights Compliance, Vulnerability and Advisor features'
111
122
 
112
123
  role 'ForemanRhCloud Read Only', read_only_permissions, 'Role granting read-only permissions to view
113
- Insights Vulnerability, Advisor, and host inventory'
124
+ Insights Compliance, Vulnerability, Advisor, and host inventory'
114
125
 
115
126
  add_permissions_to_default_roles Role::ORG_ADMIN => plugin_permissions,
116
127
  Role::MANAGER => plugin_permissions,
@@ -1,3 +1,3 @@
1
1
  module ForemanRhCloud
2
- VERSION = '14.1.3'.freeze
2
+ VERSION = '14.2.0'.freeze
3
3
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "14.1.3",
3
+ "version": "14.2.0",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -178,6 +178,61 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
178
178
  assert_nil result
179
179
  end
180
180
 
181
+ test 'scope_request? should return tag_name for compliance systems endpoint' do
182
+ get_req = ActionDispatch::Request.new(
183
+ 'REQUEST_URI' => '/api/compliance/v2/systems',
184
+ 'REQUEST_METHOD' => 'GET',
185
+ 'rack.input' => ::Puma::NullIO.new
186
+ )
187
+
188
+ result = @forwarder.send(:scope_request?, get_req, 'api/compliance/v2/systems')
189
+ assert_equal :tags, result
190
+ end
191
+
192
+ test 'scope_request? should return tag_name for compliance report systems endpoint' do
193
+ get_req = ActionDispatch::Request.new(
194
+ 'REQUEST_URI' => '/api/compliance/v2/reports/report-123/systems',
195
+ 'REQUEST_METHOD' => 'GET',
196
+ 'rack.input' => ::Puma::NullIO.new
197
+ )
198
+
199
+ result = @forwarder.send(:scope_request?, get_req, 'api/compliance/v2/reports/report-123/systems')
200
+ assert_equal :tags, result
201
+ end
202
+
203
+ test 'scope_request? should return tag_name for individual compliance system endpoint' do
204
+ get_req = ActionDispatch::Request.new(
205
+ 'REQUEST_URI' => '/api/compliance/v2/systems/system-456',
206
+ 'REQUEST_METHOD' => 'GET',
207
+ 'rack.input' => ::Puma::NullIO.new
208
+ )
209
+
210
+ result = @forwarder.send(:scope_request?, get_req, 'api/compliance/v2/systems/system-456')
211
+ assert_equal :tags, result
212
+ end
213
+
214
+ test 'scope_request? should return tag_name for system policies endpoint' do
215
+ get_req = ActionDispatch::Request.new(
216
+ 'REQUEST_URI' => '/api/compliance/v2/systems/system-456/policies',
217
+ 'REQUEST_METHOD' => 'GET',
218
+ 'rack.input' => ::Puma::NullIO.new
219
+ )
220
+
221
+ result = @forwarder.send(:scope_request?, get_req, 'api/compliance/v2/systems/system-456/policies')
222
+ assert_equal :tags, result
223
+ end
224
+
225
+ test 'scope_request? should return tag_name for system reports endpoint' do
226
+ get_req = ActionDispatch::Request.new(
227
+ 'REQUEST_URI' => '/api/compliance/v2/systems/system-456/reports',
228
+ 'REQUEST_METHOD' => 'GET',
229
+ 'rack.input' => ::Puma::NullIO.new
230
+ )
231
+
232
+ result = @forwarder.send(:scope_request?, get_req, 'api/compliance/v2/systems/system-456/reports')
233
+ assert_equal :tags, result
234
+ end
235
+
181
236
  test 'prepare_tags should use provided tag_name' do
182
237
  result = @forwarder.send(:prepare_tags, @user, @organization, @location, :custom_tag)
183
238
 
@@ -215,7 +270,7 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
215
270
 
216
271
  # Permission enforcement tests
217
272
 
218
- # GET /api/inventory/v1/hosts requires view_vulnerability
273
+ # GET /api/inventory/v1/hosts is a shared dependency - view_compliance OR view_vulnerability grants access
219
274
  test 'should allow GET request to inventory hosts when user has view_vulnerability permission' do
220
275
  user_agent = { :foo => :bar }
221
276
  params = {}
@@ -228,6 +283,7 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
228
283
  'action_dispatch.request.query_parameters' => params
229
284
  )
230
285
 
286
+ @user.stubs(:can?).with(:view_compliance).returns(false)
231
287
  @user.stubs(:can?).with(:view_vulnerability).returns(true)
232
288
  ::ForemanRhCloud::TagsAuth.any_instance.expects(:update_tag)
233
289
  @forwarder.expects(:execute_cloud_request).returns(true)
@@ -235,7 +291,18 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
235
291
  @forwarder.forward_request(req, 'api/inventory/v1/hosts', 'test_controller', @user, @organization, @location)
236
292
  end
237
293
 
238
- test 'should deny GET request to inventory hosts when user lacks view_vulnerability permission' do
294
+ test 'should allow GET request to inventory hosts when user has view_compliance permission' do
295
+ req = build_request(method: 'GET', uri: '/api/inventory/v1/hosts')
296
+
297
+ @user.stubs(:can?).with(:view_compliance).returns(true)
298
+ @user.stubs(:can?).with(:view_vulnerability).returns(false)
299
+ ::ForemanRhCloud::TagsAuth.any_instance.expects(:update_tag)
300
+ @forwarder.expects(:execute_cloud_request).returns(true)
301
+
302
+ @forwarder.forward_request(req, 'api/inventory/v1/hosts', 'test_controller', @user, @organization, @location)
303
+ end
304
+
305
+ test 'should deny GET request to inventory hosts when user lacks both view_compliance and view_vulnerability' do
239
306
  user_agent = { :foo => :bar }
240
307
  params = {}
241
308
 
@@ -247,6 +314,7 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
247
314
  'action_dispatch.request.query_parameters' => params
248
315
  )
249
316
 
317
+ @user.stubs(:can?).with(:view_compliance).returns(false)
250
318
  @user.stubs(:can?).with(:view_vulnerability).returns(false)
251
319
 
252
320
  assert_raises(::Foreman::PermissionMissingException) do
@@ -307,9 +375,43 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
307
375
 
308
376
  # Data-driven tests for required_permission_for
309
377
  PERMISSION_MAPPINGS = [
378
+ # Shared inventory hosts endpoint - either view_compliance or view_vulnerability grants GET access
379
+ { path: 'api/inventory/v1/hosts', method: 'GET', expected: [:view_compliance, :view_vulnerability] },
380
+ { path: 'api/inventory/v1/hosts/abc-123', method: 'GET', expected: [:view_compliance, :view_vulnerability] },
381
+ # Compliance endpoints
382
+ { path: 'api/compliance/v2/policies', method: 'GET', expected: :view_compliance },
383
+ { path: 'api/compliance/v2/policies', method: 'POST', expected: :edit_compliance },
384
+ { path: 'api/compliance/v2/policies/policy-123', method: 'GET', expected: :view_compliance },
385
+ { path: 'api/compliance/v2/policies/policy-123', method: 'DELETE', expected: :edit_compliance },
386
+ { path: 'api/compliance/v2/policies/policy-123', method: 'PATCH', expected: :edit_compliance },
387
+ { path: 'api/compliance/v2/policies/policy-123/systems', method: 'GET', expected: :view_compliance },
388
+ { path: 'api/compliance/v2/policies/policy-123/systems', method: 'POST', expected: :edit_compliance },
389
+ { path: 'api/compliance/v2/policies/policy-123/systems/system-456', method: 'GET', expected: :view_compliance },
390
+ { path: 'api/compliance/v2/policies/policy-123/systems/system-456', method: 'DELETE', expected: :edit_compliance },
391
+ { path: 'api/compliance/v2/policies/policy-123/systems/system-456', method: 'PATCH', expected: :edit_compliance },
392
+ { path: 'api/compliance/v2/policies/policy-123/tailorings', method: 'GET', expected: :view_compliance },
393
+ { path: 'api/compliance/v2/policies/policy-123/tailorings', method: 'POST', expected: :edit_compliance },
394
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789', method: 'GET', expected: :view_compliance },
395
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789', method: 'PATCH', expected: :edit_compliance },
396
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules', method: 'GET', expected: :view_compliance },
397
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules', method: 'POST', expected: :edit_compliance },
398
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999', method: 'GET', expected: :view_compliance },
399
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999', method: 'DELETE', expected: :edit_compliance },
400
+ { path: 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999', method: 'PATCH', expected: :edit_compliance },
401
+ { path: 'api/compliance/v2/reports', method: 'GET', expected: :view_compliance },
402
+ { path: 'api/compliance/v2/reports/report-123', method: 'GET', expected: :view_compliance },
403
+ { path: 'api/compliance/v2/reports/report-123', method: 'DELETE', expected: :edit_compliance },
404
+ { path: 'api/compliance/v2/reports/report-123/systems', method: 'GET', expected: :view_compliance },
405
+ { path: 'api/compliance/v2/reports/report-123/systems/system-456', method: 'GET', expected: :view_compliance },
406
+ { path: 'api/compliance/v2/reports/report-123/systems/os_versions', method: 'GET', expected: :view_compliance },
407
+ { path: 'api/compliance/v2/policies/policy-123/systems/os_versions', method: 'GET', expected: :view_compliance },
408
+ { path: 'api/compliance/v2/systems', method: 'GET', expected: :view_compliance },
409
+ { path: 'api/compliance/v2/systems/system-456', method: 'GET', expected: :view_compliance },
410
+ { path: 'api/compliance/v2/systems/os_versions', method: 'GET', expected: :view_compliance },
411
+ { path: 'api/compliance/v2/systems/system-456/policies', method: 'GET', expected: :view_compliance },
412
+ { path: 'api/compliance/v2/systems/system-456/reports', method: 'GET', expected: :view_compliance },
413
+ { path: 'api/compliance/v2/profiles', method: 'GET', expected: :view_compliance },
310
414
  # Vulnerability endpoints
311
- { path: 'api/inventory/v1/hosts', method: 'GET', expected: :view_vulnerability },
312
- { path: 'api/inventory/v1/hosts/abc-123', method: 'GET', expected: :view_vulnerability },
313
415
  { path: 'api/vulnerability/v1/vulnerabilities/cves', method: 'POST', expected: :view_vulnerability },
314
416
  { path: 'api/vulnerability/v1/status', method: 'PATCH', expected: :edit_vulnerability },
315
417
  { path: 'api/vulnerability/v1/cves/status', method: 'PATCH', expected: :edit_vulnerability },
@@ -383,6 +485,140 @@ class UIRequestForwarderTest < ActiveSupport::TestCase
383
485
  end
384
486
  end
385
487
 
488
+ # Compliance permission integration tests
489
+ test 'should allow GET request to compliance policies when user has view_compliance permission' do
490
+ req = build_request(method: 'GET', uri: '/api/compliance/v2/policies')
491
+
492
+ @user.stubs(:can?).with(:view_compliance).returns(true)
493
+ @forwarder.expects(:execute_cloud_request).returns(true)
494
+
495
+ @forwarder.forward_request(req, 'api/compliance/v2/policies', 'test_controller', @user, @organization, @location)
496
+ end
497
+
498
+ test 'should deny GET request to compliance policies when user lacks view_compliance permission' do
499
+ req = build_request(method: 'GET', uri: '/api/compliance/v2/policies')
500
+
501
+ @user.stubs(:can?).with(:view_compliance).returns(false)
502
+
503
+ assert_raises(::Foreman::PermissionMissingException) do
504
+ @forwarder.forward_request(req, 'api/compliance/v2/policies', 'test_controller', @user, @organization, @location)
505
+ end
506
+ end
507
+
508
+ test 'should allow POST request to compliance policies when user has edit_compliance permission' do
509
+ req = build_request(method: 'POST', uri: '/api/compliance/v2/policies', data: '{"name": "test-policy"}')
510
+
511
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
512
+ @forwarder.expects(:execute_cloud_request).returns(true)
513
+
514
+ @forwarder.forward_request(req, 'api/compliance/v2/policies', 'test_controller', @user, @organization, @location)
515
+ end
516
+
517
+ test 'should deny POST request to compliance policies when user lacks edit_compliance permission' do
518
+ req = build_request(method: 'POST', uri: '/api/compliance/v2/policies', data: '{"name": "test-policy"}')
519
+
520
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
521
+
522
+ assert_raises(::Foreman::PermissionMissingException) do
523
+ @forwarder.forward_request(req, 'api/compliance/v2/policies', 'test_controller', @user, @organization, @location)
524
+ end
525
+ end
526
+
527
+ test 'should allow PATCH request to compliance policy when user has edit_compliance permission' do
528
+ req = build_request(method: 'PATCH', uri: '/api/compliance/v2/policies/policy-123', data: '{"name": "updated-policy"}')
529
+
530
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
531
+ @forwarder.expects(:execute_cloud_request).returns(true)
532
+
533
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123', 'test_controller', @user, @organization, @location)
534
+ end
535
+
536
+ test 'should deny PATCH request to compliance policy when user lacks edit_compliance permission' do
537
+ req = build_request(method: 'PATCH', uri: '/api/compliance/v2/policies/policy-123', data: '{"name": "updated-policy"}')
538
+
539
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
540
+
541
+ assert_raises(::Foreman::PermissionMissingException) do
542
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123', 'test_controller', @user, @organization, @location)
543
+ end
544
+ end
545
+
546
+ test 'should allow DELETE request to compliance policy when user has edit_compliance permission' do
547
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/policies/policy-123')
548
+
549
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
550
+ @forwarder.expects(:execute_cloud_request).returns(true)
551
+
552
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123', 'test_controller', @user, @organization, @location)
553
+ end
554
+
555
+ test 'should deny DELETE request to compliance policy when user lacks edit_compliance permission' do
556
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/policies/policy-123')
557
+
558
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
559
+
560
+ assert_raises(::Foreman::PermissionMissingException) do
561
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123', 'test_controller', @user, @organization, @location)
562
+ end
563
+ end
564
+
565
+ test 'should allow POST request to compliance policy systems when user has edit_compliance permission' do
566
+ req = build_request(method: 'POST', uri: '/api/compliance/v2/policies/policy-123/systems', data: '{"id": "system-456"}')
567
+
568
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
569
+ @forwarder.expects(:execute_cloud_request).returns(true)
570
+
571
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123/systems', 'test_controller', @user, @organization, @location)
572
+ end
573
+
574
+ test 'should deny POST request to compliance policy systems when user lacks edit_compliance permission' do
575
+ req = build_request(method: 'POST', uri: '/api/compliance/v2/policies/policy-123/systems', data: '{"id": "system-456"}')
576
+
577
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
578
+
579
+ assert_raises(::Foreman::PermissionMissingException) do
580
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123/systems', 'test_controller', @user, @organization, @location)
581
+ end
582
+ end
583
+
584
+ test 'should allow DELETE request to compliance report when user has edit_compliance permission' do
585
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/reports/report-123')
586
+
587
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
588
+ @forwarder.expects(:execute_cloud_request).returns(true)
589
+
590
+ @forwarder.forward_request(req, 'api/compliance/v2/reports/report-123', 'test_controller', @user, @organization, @location)
591
+ end
592
+
593
+ test 'should deny DELETE request to compliance report when user lacks edit_compliance permission' do
594
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/reports/report-123')
595
+
596
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
597
+
598
+ assert_raises(::Foreman::PermissionMissingException) do
599
+ @forwarder.forward_request(req, 'api/compliance/v2/reports/report-123', 'test_controller', @user, @organization, @location)
600
+ end
601
+ end
602
+
603
+ test 'should allow DELETE request to compliance tailoring rule when user has edit_compliance permission' do
604
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999')
605
+
606
+ @user.stubs(:can?).with(:edit_compliance).returns(true)
607
+ @forwarder.expects(:execute_cloud_request).returns(true)
608
+
609
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999', 'test_controller', @user, @organization, @location)
610
+ end
611
+
612
+ test 'should deny DELETE request to compliance tailoring rule when user lacks edit_compliance permission' do
613
+ req = build_request(method: 'DELETE', uri: '/api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999')
614
+
615
+ @user.stubs(:can?).with(:edit_compliance).returns(false)
616
+
617
+ assert_raises(::Foreman::PermissionMissingException) do
618
+ @forwarder.forward_request(req, 'api/compliance/v2/policies/policy-123/tailorings/tailoring-789/rules/rule-999', 'test_controller', @user, @organization, @location)
619
+ end
620
+ end
621
+
386
622
  # Edge case: anonymous user
387
623
  test 'should deny request when user is nil' do
388
624
  req = build_request(method: 'GET', uri: '/api/insights/v1/stats/systems')
@@ -32,11 +32,6 @@ export const processStatusName = 'upload_report_status';
32
32
 
33
33
  export const filterTerm = 'some_filter';
34
34
 
35
- export const CloudConnectorStatus = {
36
- id: 7,
37
- task: { id: 11 },
38
- };
39
-
40
35
  export const props = {
41
36
  accounts,
42
37
  fetchAccountsStatus: noop,
@@ -47,7 +42,6 @@ export const props = {
47
42
 
48
43
  export const pollingResponse = {
49
44
  accounts,
50
- CloudConnectorStatus,
51
45
  };
52
46
 
53
47
  export const fetchAccountsStatusResponse = {
@@ -20,7 +20,6 @@ export default (state = initialState, action) => {
20
20
  accounts,
21
21
  accountID,
22
22
  processStatusName,
23
- CloudConnectorStatus,
24
23
  } = {},
25
24
  } = action;
26
25
 
@@ -29,7 +28,6 @@ export default (state = initialState, action) => {
29
28
  return state.merge({
30
29
  ...state,
31
30
  accounts,
32
- CloudConnectorStatus,
33
31
  error: null,
34
32
  });
35
33
  case INVENTORY_ACCOUNT_STATUS_POLLING_ERROR:
@@ -13,7 +13,6 @@ import {
13
13
  processStatusName,
14
14
  pollingResponse,
15
15
  accounts,
16
- CloudConnectorStatus,
17
16
  } from '../AccountList.fixtures';
18
17
 
19
18
  describe('AccountList reducer', () => {
@@ -30,7 +29,6 @@ describe('AccountList reducer', () => {
30
29
  payload: pollingResponse,
31
30
  });
32
31
  expect(state.accounts).toEqual(accounts);
33
- expect(state.CloudConnectorStatus).toEqual(CloudConnectorStatus);
34
32
  expect(state.error).toBeNull();
35
33
  });
36
34
 
@@ -4,7 +4,6 @@ import InventorySettings from '../InventorySettings';
4
4
  import PageDescription from './components/PageDescription';
5
5
  import InventoryFilter from '../InventoryFilter';
6
6
  import ToolbarButtons from './components/ToolbarButtons';
7
- import SettingsWarning from './components/SettingsWarning';
8
7
  import PageTitle from './PageTitle';
9
8
  import { useIopConfig } from '../../../common/Hooks/ConfigHooks';
10
9
  import './PageHeader.scss';
@@ -14,7 +13,6 @@ const PageHeader = () => {
14
13
 
15
14
  return (
16
15
  <div className="inventory-upload-header">
17
- <SettingsWarning />
18
16
  <PageTitle />
19
17
  {!isIop && (
20
18
  <div className="inventory-upload-header-description">
@@ -1,6 +1,6 @@
1
1
  .rh-cloud-inventory-page {
2
2
  .inventory-upload-header {
3
- margin-top: 35px;
3
+ padding: var(--pf-v5-global--spacer--md) var(--pf-v5-global--spacer--lg) var(--pf-v5-global--spacer--lg) var(--pf-v5-global--spacer--lg);
4
4
 
5
5
  h1 {
6
6
  font-family: 'RedHatDisplay';
@@ -33,7 +33,6 @@
33
33
  }
34
34
 
35
35
  .inventory-upload-header-title {
36
- margin-top: -15px;
37
36
  margin-bottom: 8px;
38
37
  }
39
38