foreman_openscap 4.3.3 → 5.0.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/compliance/arf_reports_controller.rb +0 -6
  3. data/app/helpers/arf_report_dashboard_helper.rb +2 -4
  4. data/app/helpers/compliance_hosts_helper.rb +1 -1
  5. data/app/helpers/policies_helper.rb +1 -1
  6. data/app/services/foreman_openscap/client_config/base.rb +1 -0
  7. data/app/services/foreman_openscap/client_config/puppet.rb +6 -2
  8. data/app/views/arf_reports/_metrics.html.erb +4 -4
  9. data/app/views/compliance_hosts/show.html.erb +4 -6
  10. data/app/views/dashboard/_compliance_reports_breakdown_widget.html.erb +4 -3
  11. data/app/views/policy_dashboard/_policy_chart_widget.html.erb +3 -2
  12. data/db/migrate/20200117135424_migrate_port_overrides_to_int.rb +2 -1
  13. data/db/migrate/20201202110213_update_puppet_port_param_type.rb +2 -1
  14. data/lib/foreman_openscap/engine.rb +0 -7
  15. data/lib/foreman_openscap/version.rb +1 -1
  16. data/test/functional/api/v2/compliance/oval_reports_controller_test.rb +1 -1
  17. data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -0
  18. data/test/helpers/arf_report_dashboard_helper_test.rb +9 -10
  19. data/test/helpers/policy_dashboard_helper_test.rb +1 -1
  20. data/test/test_plugin_helper.rb +9 -4
  21. data/test/unit/policy_test.rb +1 -1
  22. data/test/unit/services/config_name_service_test.rb +1 -0
  23. data/test/unit/services/hostgroup_overrider_test.rb +2 -1
  24. data/test/unit/services/lookup_key_overrider_test.rb +4 -1
  25. data/webpack/components/EmptyState.js +9 -3
  26. data/webpack/components/withLoading.js +20 -1
  27. data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
  28. data/webpack/graphql/queries/cves.gql +5 -0
  29. data/webpack/graphql/queries/ovalContents.gql +5 -0
  30. data/webpack/graphql/queries/ovalPolicies.gql +5 -0
  31. data/webpack/graphql/queries/ovalPolicy.gql +5 -0
  32. data/webpack/helpers/permissionsHelper.js +42 -0
  33. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +1 -0
  34. data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +10 -2
  35. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +91 -77
  36. data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +26 -0
  37. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +1 -0
  38. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +67 -33
  39. data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +20 -0
  40. data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +1 -0
  41. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +12 -3
  42. data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +17 -0
  43. data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +1 -0
  44. data/webpack/testHelper.js +34 -2
  45. metadata +9 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9bed6dedc6b23fd08b1c0fd3143de20e86c8aa2e65dbc0a00d0abaad21cc393
4
- data.tar.gz: 352a6052c7386969b55b241b57e3d9fa63b6ec07fc9d01d5a8ba6c4b79e7724d
3
+ metadata.gz: 534b669988c89b1335565f67c02b63443b0cfd36636749397b50617c4797316b
4
+ data.tar.gz: 4ee455ef101cc0bb6b034f1b081e2d604848620e7438e3ae887493ee77d13015
5
5
  SHA512:
6
- metadata.gz: 6dd1df925f9212a7afab91c449a9e0482427fde4fb8adfca8a620482124caed5584a2c71d1167c09308f124d5a19fb4ee0102bff4a89e91d30c81a6ac4ed01ea
7
- data.tar.gz: c16c1caec4bde07fb8d9da968859f21d2d7f42c5ec5b09c5e9560053dd22490f0ac299405a55b76fa1d4b08d1417b13233799b085fe56b46b8bb13829827394e
6
+ metadata.gz: b2ddaa5f34a78a9f085f5564f7e12f28071baf8b1679e645c327bb6d229986f0b399ae9a3bf50078e7d4c536014b43df974125851277ff1584130918b93382a2
7
+ data.tar.gz: 6a115b58c74b88f4c9e4f81ab5c31a0db2195a285d2c2eb4612251aead4131a426b34aab2593b7e8b6e2b656fee92cae0adc1b3d5bd19787466514e35312fd70
@@ -140,12 +140,6 @@ module Api
140
140
  super
141
141
  end
142
142
  end
143
-
144
- protected
145
-
146
- def assign_lone_taxonomies
147
- # do not assign lone taxonomies to arf report
148
- end
149
143
  end
150
144
  end
151
145
  end
@@ -6,11 +6,9 @@ module ArfReportDashboardHelper
6
6
  }.freeze
7
7
 
8
8
  def breakdown_chart_data(categories, report, colors = COLORS)
9
- data = categories.reduce([]) do |memo, (key, value)|
9
+ categories.reduce([]) do |memo, (key, value)|
10
10
  memo << [value, report[key], colors[key]]
11
11
  end
12
-
13
- data.to_json
14
12
  end
15
13
 
16
14
  def donut_breakdown_chart_data(report)
@@ -27,6 +25,6 @@ module ArfReportDashboardHelper
27
25
  :data => status.to_a,
28
26
  :yAxisLabel => _("Number of Events"),
29
27
  :xAxisLabel => _("Rule Results"),
30
- }.to_json
28
+ }
31
29
  end
32
30
  end
@@ -16,7 +16,7 @@ module ComplianceHostsHelper
16
16
  [_("Othered"), othered, ArfReportDashboardHelper::COLORS[:othered]],
17
17
  ['dates', date, nil]
18
18
  ]
19
- { :data => data, :xAxisDataLabel => 'dates', :config => 'timeseries' }.to_json
19
+ { :data => data, :xAxisDataLabel => 'dates', :config => 'timeseries' }
20
20
  end
21
21
 
22
22
  def compliance_host_multiple_actions
@@ -41,7 +41,7 @@ module PoliciesHelper
41
41
  end
42
42
 
43
43
  def deploy_by_radio_checked(policy, tool)
44
- type = policy.deploy_by ? policy.deploy_by.to_sym : :puppet
44
+ type = policy.deploy_by ? policy.deploy_by.to_sym : :manual
45
45
  tool.type == type
46
46
  end
47
47
 
@@ -38,6 +38,7 @@ module ForemanOpenscap
38
38
  end
39
39
 
40
40
  def find_config_item(scope = config_item_class_name.constantize)
41
+ return unless scope
41
42
  return scope.find_by :name => config_item_name if scope.respond_to?(:find_by)
42
43
  # all_puppetclasses, all_ansible_roles methods return Array, not ActiveRecord::Relation
43
44
  scope.find { |item| item.name == config_item_name }
@@ -10,7 +10,7 @@ module ForemanOpenscap
10
10
  end
11
11
 
12
12
  def available?
13
- defined?(Puppetclass)
13
+ defined?(ForemanPuppet)
14
14
  end
15
15
 
16
16
  def inline_help
@@ -21,13 +21,17 @@ module ForemanOpenscap
21
21
  }
22
22
  end
23
23
 
24
+ def collection_method
25
+ :puppetclasses
26
+ end
27
+
24
28
  def constants
25
29
  OpenStruct.new(
26
30
  :server_param => 'server',
27
31
  :port_param => 'port',
28
32
  :policies_param => 'policies',
29
33
  :puppetclass_name => 'foreman_scap_client',
30
- :config_item_class_name => 'Puppetclass',
34
+ :config_item_class_name => 'ForemanPuppet::Puppetclass',
31
35
  :override_method_name => 'class_params',
32
36
  :msg_name => _('Puppet class'),
33
37
  :lookup_key_plural_name => _('Smart Class Parameters'),
@@ -4,15 +4,15 @@
4
4
  <div class="col-md-5 scap-breakdown-chart-col">
5
5
  <div class="stats-well">
6
6
  <h4 class="ca" ><%= _('Report Metrics') %></h4>
7
- <div id="arf-report-breakdown-chart" class="scap-breakdown-chart"></div>
8
- <%= mount_react_component('DonutChart', "#arf-report-breakdown-chart", donut_breakdown_chart_data(metrics)) %>
7
+ <div class="scap-breakdown-chart">
8
+ <%= react_component('DonutChart', :data => donut_breakdown_chart_data(metrics)) %>
9
+ </div>
9
10
  </div>
10
11
  </div>
11
12
  <div class="col-md-5 arf-report-rule-chart-col">
12
13
  <div class="stats-well">
13
14
  <h4 class="ca" ><%= _('Report Status') %></h4>
14
- <div id="arf-report-rule-chart"></div>
15
- <%= mount_react_component('BarChart', "#arf-report-rule-chart", arf_report_status_chart_data(status), :flatten_data => true) %>
15
+ <%= react_component('BarChart', arf_report_status_chart_data(status)) %>
16
16
  </div>
17
17
  </div>
18
18
  <div class="col-md-2">
@@ -17,18 +17,16 @@
17
17
  hash_for_arf_report_path(:id => data.latest_report.id)
18
18
  .merge(:auth_object => data.latest_report)) %></h4>
19
19
  <% report = data.report %>
20
- <% id = "host-policy-breakdown-chart-#{policy.id}" %>
21
- <div id="<%= id %>" class="scap-breakdown-chart"></div>
22
- <%= mount_react_component('DonutChart', "##{id}", donut_breakdown_chart_data(report)) %>
20
+ <div class="scap-breakdown-chart">
21
+ <%= react_component('DonutChart', :data => donut_breakdown_chart_data(report)) %>
22
+ </div>
23
23
  </div>
24
24
  </div>
25
25
 
26
26
  <div class="col-md-8">
27
27
  <div class="stats-well">
28
28
  <h4 class="ca"><%= _("%s reports over time") % policy %></h4>
29
- <% reports_graph_id = "arf-reports-over-time-#{policy.id}" %>
30
- <div id="<%= reports_graph_id %>"></div>
31
- <%= mount_react_component('LineChart', "##{reports_graph_id}", host_arf_reports_chart_data(policy.id), :flatten_data => true) %>
29
+ <%= react_component('LineChart', host_arf_reports_chart_data(policy.id)) %>
32
30
  </div>
33
31
  </div>
34
32
  <% else %>
@@ -1,4 +1,5 @@
1
1
  <h4 class="header ca"><%= _('Compliance Reports Breakdown') %></h4>
2
- <div id="compliance-reports-breakdown" class="host-configuration-chart"/>
3
- <% report = ForemanOpenscap::ReportDashboard::Data.new().report %>
4
- <%= mount_react_component('DonutChart', "#compliance-reports-breakdown", donut_breakdown_chart_data(report)) %>
2
+ <div class="host-configuration-chart">
3
+ <% report = ForemanOpenscap::ReportDashboard::Data.new().report %>
4
+ <%= react_component('DonutChart', :data => donut_breakdown_chart_data(report)) %>
5
+ </div>
@@ -2,6 +2,7 @@
2
2
 
3
3
  <div id='status-chart'>
4
4
  <h4 class="header ca"><%= _('Host Breakdown Chart') %></h4>
5
- <div id="policy-breakdown-chart" class="scap-breakdown-chart"></div>
6
- <%= mount_react_component('DonutChart', "#policy-breakdown-chart", policy_breakdown_chart_data(@report)) %>
5
+ <div class="scap-breakdown-chart">
6
+ <%= react_component('DonutChart', :data => policy_breakdown_chart_data(@report)) %>
7
+ </div>
7
8
  </div>
@@ -10,7 +10,8 @@ class MigratePortOverridesToInt < ActiveRecord::Migration[5.2]
10
10
  private
11
11
 
12
12
  def transform_lookup_values(method)
13
- puppet_class = Puppetclass.find_by :name => 'foreman_scap_client'
13
+ return unless defined?(ForemanPuppet)
14
+ puppet_class = ::ForemanPuppet::Puppetclass.find_by :name => 'foreman_scap_client'
14
15
  return unless puppet_class
15
16
  port_key = puppet_class.class_params.find_by :key => 'port'
16
17
  return unless port_key
@@ -10,7 +10,8 @@ class UpdatePuppetPortParamType < ActiveRecord::Migration[6.0]
10
10
  private
11
11
 
12
12
  def update_port_type(method)
13
- puppet_class = Puppetclass.find_by :name => 'foreman_scap_client'
13
+ return unless defined?(ForemanPuppet)
14
+ puppet_class = ::ForemanPuppet::Puppetclass.find_by :name => 'foreman_scap_client'
14
15
  return unless puppet_class
15
16
  port_key = puppet_class.class_params.find_by :key => 'port'
16
17
  return unless port_key
@@ -223,13 +223,6 @@ module ForemanOpenscap
223
223
  register_graphql_query_field :oval_policy, '::Types::OvalPolicy', :record_field
224
224
  register_graphql_query_field :cves, '::Types::Cve', :collection_field
225
225
 
226
- # move to core
227
- extend_graphql_type type: ::Types::Hostgroup do
228
- field :descendants, Types::Hostgroup.connection_type, null: true, resolve: (proc do |object|
229
- RecordLoader.for(model_class).load_many(object.descendant_ids)
230
- end)
231
- end
232
-
233
226
  register_facet ForemanOpenscap::Host::OvalFacet, :oval_facet do
234
227
  configure_host do
235
228
  extend_model ForemanOpenscap::OvalFacetHostExtensions
@@ -1,3 +1,3 @@
1
1
  module ForemanOpenscap
2
- VERSION = "4.3.3".freeze
2
+ VERSION = "5.0.0".freeze
3
3
  end
@@ -20,7 +20,7 @@ class Api::V2::Compliance::OvalReportsControllerTest < ActionController::TestCas
20
20
 
21
21
  test 'should show host errors on CVEs upload' do
22
22
  proxy = FactoryBot.create(:smart_proxy)
23
- host = FactoryBot.create(:host, :puppet_proxy => proxy, :environment => FactoryBot.create(:environment))
23
+ host = FactoryBot.create(:host, :puppet_proxy => proxy)
24
24
  SmartProxy.any_instance.stubs(:smart_proxy_features).returns([])
25
25
  post :create, :params => @params.merge(:cname => host.name), :session => set_session_user
26
26
 
@@ -40,6 +40,7 @@ class Api::V2::Compliance::PoliciesControllerTest < ActionController::TestCase
40
40
  end
41
41
 
42
42
  test "should get index and show hostgroups" do
43
+ skip unless puppet_available?
43
44
  ForemanOpenscap::Policy.any_instance.stubs(:find_scap_puppetclass).returns(FactoryBot.create(:puppetclass, :name => 'foreman_scap_client'))
44
45
  ForemanOpenscap::Policy.any_instance.stubs(:populate_overrides)
45
46
  hostgroup = FactoryBot.create(:hostgroup)
@@ -61,6 +62,7 @@ class Api::V2::Compliance::PoliciesControllerTest < ActionController::TestCase
61
62
  end
62
63
 
63
64
  test "should show a policy hosts and hostgroups" do
65
+ skip unless puppet_available?
64
66
  ForemanOpenscap::Policy.any_instance.stubs(:find_scap_puppetclass).returns(FactoryBot.create(:puppetclass, :name => 'foreman_scap_client'))
65
67
  ForemanOpenscap::Policy.any_instance.stubs(:populate_overrides)
66
68
  hostgroup = FactoryBot.create(:hostgroup)
@@ -7,14 +7,13 @@ class ArfReportDashboardHelperTest < ActionView::TestCase
7
7
  categories = { :passed => 'passed', :failed => 'failed' }
8
8
  report = { :passed => 23, :failed => 24 }
9
9
  colors = { :passed => '#FFF', :failed => '#000' }
10
- res = JSON.parse(breakdown_chart_data(categories, report, colors))
10
+ res = breakdown_chart_data(categories, report, colors)
11
11
  assert_equal ["passed", 23, "#FFF"], res.first
12
12
  assert_equal ["failed", 24, "#000"], res.last
13
13
  end
14
14
 
15
15
  test 'should return breakdown chart data for donut as json' do
16
- report = { :passed => 4, :failed => 7, :othered => 5 }
17
- res = JSON.parse(donut_breakdown_chart_data(report))
16
+ res = donut_breakdown_chart_data(:passed => 4, :failed => 7, :othered => 5)
18
17
  assert_equal 3, res.size
19
18
  assert_include res, ["Passed", 4, ArfReportDashboardHelper::COLORS[:passed]]
20
19
  assert_include res, ["Failed", 7, ArfReportDashboardHelper::COLORS[:failed]]
@@ -22,12 +21,12 @@ class ArfReportDashboardHelperTest < ActionView::TestCase
22
21
  end
23
22
 
24
23
  test 'should return data for report status chart' do
25
- res = JSON.parse(arf_report_status_chart_data(:passed => 6, :failed => 7, :othered => 8))
26
- assert_equal "Number of Events", res['yAxisLabel']
27
- assert_equal "Rule Results", res['xAxisLabel']
28
- assert_equal 3, res['data'].size
29
- assert_include res['data'], ["passed", 6]
30
- assert_include res['data'], ["failed", 7]
31
- assert_include res['data'], ["othered", 8]
24
+ res = arf_report_status_chart_data(:passed => 6, :failed => 7, :othered => 8)
25
+ assert_equal "Number of Events", res[:yAxisLabel]
26
+ assert_equal "Rule Results", res[:xAxisLabel]
27
+ assert_equal 3, res[:data].size
28
+ assert_include res[:data], [:passed, 6]
29
+ assert_include res[:data], [:failed, 7]
30
+ assert_include res[:data], [:othered, 8]
32
31
  end
33
32
  end
@@ -11,7 +11,7 @@ class PolicyDashboardHelperTest < ActionView::TestCase
11
11
  :inconclusive_hosts => 7,
12
12
  :report_missing => 8
13
13
  }
14
- res = JSON.parse(policy_breakdown_chart_data(report))
14
+ res = policy_breakdown_chart_data(report)
15
15
  assert_equal 4, res.size
16
16
  assert_include res, ['Compliant hosts', 5, PolicyDashboardHelper::COLORS[:compliant_hosts]]
17
17
  assert_include res, ['Incompliant hosts', 6, PolicyDashboardHelper::COLORS[:incompliant_hosts]]
@@ -5,19 +5,24 @@ require 'test_helper'
5
5
  FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
6
6
  # Add factories from foreman_ansible
7
7
  FactoryBot.definition_file_paths << File.join(ForemanAnsible::Engine.root, '/test/factories')
8
+ FactoryBot.definition_file_paths << File.join(ForemanPuppet::Engine.root, '/test/factories') if defined?(ForemanPuppet)
8
9
  FactoryBot.reload
9
10
 
10
11
  require "#{ForemanOpenscap::Engine.root}/test/fixtures/cve_fixtures"
11
12
 
12
13
  module ScapClientPuppetclass
14
+ def puppet_available?
15
+ defined?(ForemanPuppet)
16
+ end
17
+
13
18
  def setup_puppet_class
14
19
  puppet_config = ::ForemanOpenscap::ClientConfig::Puppet.new
15
- Puppetclass.find_by(:name => puppet_config.puppetclass_name)&.destroy
20
+ ForemanPuppet::Puppetclass.find_by(:name => puppet_config.puppetclass_name)&.destroy
16
21
 
17
22
  puppet_class = FactoryBot.create(:puppetclass, :name => puppet_config.puppetclass_name)
18
- server_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.server_param, :default_value => nil)
19
- port_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.port_param, :default_value => nil)
20
- policies_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.policies_param, :default_value => nil)
23
+ server_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.server_param, :default_value => nil, :override => false)
24
+ port_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.port_param, :default_value => nil, :override => false)
25
+ policies_param = FactoryBot.create(:puppetclass_lookup_key, :key => puppet_config.policies_param, :default_value => nil, :override => false)
21
26
 
22
27
  env = FactoryBot.create :environment
23
28
 
@@ -75,7 +75,6 @@ class PolicyTest < ActiveSupport::TestCase
75
75
  asset = FactoryBot.create(:asset, :assetable_id => hg.id, :assetable_type => 'Hostgroup')
76
76
  policy = FactoryBot.create(:policy, :assets => [asset], :scap_content => @scap_content, :scap_content_profile => @scap_profile)
77
77
  policy.save!
78
- hg.hostgroup_classes.destroy_all
79
78
  hg.destroy
80
79
  assert_equal 0, policy.hostgroups.count
81
80
  end
@@ -314,6 +313,7 @@ class PolicyTest < ActiveSupport::TestCase
314
313
  end
315
314
 
316
315
  test "should change deploy type" do
316
+ skip unless puppet_available?
317
317
  policy = FactoryBot.create(:policy, :scap_content => @scap_content, :scap_content_profile => @scap_profile)
318
318
  setup_puppet_class
319
319
  policy.change_deploy_type({ :deploy_by => 'puppet' })
@@ -24,6 +24,7 @@ class ConfigNameServiceTest < ActiveSupport::TestCase
24
24
  end
25
25
 
26
26
  test 'should find all available except Manual' do
27
+ skip unless puppet_available?
27
28
  ForemanOpenscap::ClientConfig::Ansible.any_instance.stubs(:available?).returns(false)
28
29
  configs = @name_service.all_available_except(:manual)
29
30
  assert_equal 1, configs.size
@@ -9,11 +9,12 @@ class HostgroupOverriderTest < ActiveSupport::TestCase
9
9
  end
10
10
 
11
11
  test 'should populate puppet overrides' do
12
+ skip unless puppet_available?
12
13
  puppet_class, env, port_param, server_param = setup_puppet_class.values_at :puppet_class, :env, :port_param, :server_param
13
14
 
14
15
  proxy = FactoryBot.create(:openscap_proxy, :url => 'https://override-keys.example.com:8998')
15
16
 
16
- hostgroup = FactoryBot.create(:hostgroup, :environment_id => env.id, :openscap_proxy_id => proxy.id)
17
+ hostgroup = FactoryBot.create(:hostgroup, :environment_id => env.id, :openscap_proxy_id => proxy.id, :puppet => FactoryBot.create(:hostgroup_puppet_facet))
17
18
  refute hostgroup.puppetclasses.include? puppet_class
18
19
  assert LookupValue.where(:match => "hostgroup=#{hostgroup.to_label}",
19
20
  :lookup_key_id => port_param.id,
@@ -8,6 +8,7 @@ class LookupKeyOverriderTest < ActiveSupport::TestCase
8
8
  end
9
9
 
10
10
  test 'should override puppet class parameters' do
11
+ skip unless puppet_available?
11
12
  server_param, port_param, policies_param = setup_puppet_class.values_at :server_param, :port_param, :policies_param
12
13
  refute server_param.override
13
14
  refute port_param.override
@@ -21,7 +22,8 @@ class LookupKeyOverriderTest < ActiveSupport::TestCase
21
22
  end
22
23
 
23
24
  test 'should add error when no puppet class found' do
24
- puppet_class = Puppetclass.find_by :name => ForemanOpenscap::ClientConfig::Puppet.new.puppetclass_name
25
+ skip unless puppet_available?
26
+ puppet_class = ::ForemanPuppet::Puppetclass.find_by :name => ForemanOpenscap::ClientConfig::Puppet.new.puppetclass_name
25
27
  puppet_class.destroy if puppet_class
26
28
  policy = FactoryBot.create(:policy, :scap_content => @scap_content, :scap_content_profile => @scap_content_profile, :deploy_by => :puppet)
27
29
  ForemanOpenscap::LookupKeyOverrider.new(policy).override
@@ -41,6 +43,7 @@ class LookupKeyOverriderTest < ActiveSupport::TestCase
41
43
  end
42
44
 
43
45
  test 'should add error when lookup keys not present' do
46
+ skip unless puppet_available?
44
47
  server_param, port_param, policies_param = setup_puppet_class.values_at :server_param, :port_param, :policies_param
45
48
  server_param.destroy
46
49
  port_param.destroy
@@ -12,10 +12,11 @@ import {
12
12
  CubeIcon,
13
13
  ExclamationCircleIcon,
14
14
  SearchIcon,
15
+ LockIcon,
15
16
  } from '@patternfly/react-icons';
16
17
  import { global_danger_color_200 as dangerColor } from '@patternfly/react-tokens';
17
18
 
18
- const EmptyStateIcon = ({ error, search }) => {
19
+ const EmptyStateIcon = ({ error, search, lock }) => {
19
20
  if (error)
20
21
  return (
21
22
  <PfEmptyStateIcon
@@ -23,14 +24,15 @@ const EmptyStateIcon = ({ error, search }) => {
23
24
  color={dangerColor.value}
24
25
  />
25
26
  );
27
+ if (lock) return <PfEmptyStateIcon icon={LockIcon} />;
26
28
  if (search) return <PfEmptyStateIcon icon={SearchIcon} />;
27
29
  return <PfEmptyStateIcon icon={CubeIcon} />;
28
30
  };
29
31
 
30
- const EmptyState = ({ title, body, error, search }) => (
32
+ const EmptyState = ({ title, body, error, search, lock }) => (
31
33
  <Bullseye>
32
34
  <PfEmptyState variant={EmptyStateVariant.small}>
33
- <EmptyStateIcon error={!!error} search={search} />
35
+ <EmptyStateIcon error={!!error} search={search} lock={lock} />
34
36
  <Title headingLevel="h2" size="lg">
35
37
  {title}
36
38
  </Title>
@@ -42,11 +44,13 @@ const EmptyState = ({ title, body, error, search }) => (
42
44
  EmptyStateIcon.propTypes = {
43
45
  error: PropTypes.bool,
44
46
  search: PropTypes.bool,
47
+ lock: PropTypes.bool,
45
48
  };
46
49
 
47
50
  EmptyStateIcon.defaultProps = {
48
51
  error: false,
49
52
  search: false,
53
+ lock: false,
50
54
  };
51
55
 
52
56
  EmptyState.propTypes = {
@@ -54,6 +58,7 @@ EmptyState.propTypes = {
54
58
  body: PropTypes.string,
55
59
  error: PropTypes.oneOfType([PropTypes.shape({}), PropTypes.string]),
56
60
  search: PropTypes.bool,
61
+ lock: PropTypes.bool,
57
62
  };
58
63
 
59
64
  EmptyState.defaultProps = {
@@ -62,6 +67,7 @@ EmptyState.defaultProps = {
62
67
  'There was an error retrieving data from the server. Check your connection and try again.',
63
68
  error: undefined,
64
69
  search: false,
70
+ lock: false,
65
71
  };
66
72
 
67
73
  export default EmptyState;
@@ -1,8 +1,12 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
-
5
4
  import Loading from 'foremanReact/components/Loading';
5
+ import {
6
+ permissionCheck,
7
+ permissionDeniedMsg,
8
+ } from '../helpers/permissionsHelper';
9
+
6
10
  import EmptyState from './EmptyState';
7
11
 
8
12
  const errorStateTitle = __('Error!');
@@ -24,6 +28,7 @@ const withLoading = Component => {
24
28
  resultPath,
25
29
  renameData,
26
30
  emptyStateTitle,
31
+ permissions,
27
32
  ...rest
28
33
  }) => {
29
34
  const { loading, error, data } = fetchFn(rest);
@@ -42,6 +47,18 @@ const withLoading = Component => {
42
47
  );
43
48
  }
44
49
 
50
+ const check = permissionCheck(data.currentUser, permissions);
51
+
52
+ if (!check.allowed) {
53
+ return (
54
+ <EmptyState
55
+ lock
56
+ title={__('Permission denied')}
57
+ body={permissionDeniedMsg(check.permissions.map(item => item.name))}
58
+ />
59
+ );
60
+ }
61
+
45
62
  const result = pluckData(data, resultPath);
46
63
 
47
64
  if ((Array.isArray(result) && result.length === 0) || !result) {
@@ -56,10 +73,12 @@ const withLoading = Component => {
56
73
  resultPath: PropTypes.string.isRequired,
57
74
  renameData: PropTypes.func,
58
75
  emptyStateTitle: PropTypes.string.isRequired,
76
+ permissions: PropTypes.array,
59
77
  };
60
78
 
61
79
  Subcomponent.defaultProps = {
62
80
  renameData: data => data,
81
+ permissions: [],
63
82
  };
64
83
 
65
84
  return Subcomponent;
@@ -0,0 +1,11 @@
1
+ fragment CurrentUserAttributes on User {
2
+ id
3
+ login
4
+ admin
5
+ permissions {
6
+ nodes {
7
+ id
8
+ name
9
+ }
10
+ }
11
+ }
@@ -1,3 +1,5 @@
1
+ #import "./currentUserAttributes.gql"
2
+
1
3
  query($search: String, $first: Int, $last: Int) {
2
4
  cves(search: $search, first: $first, last: $last) {
3
5
  totalCount
@@ -15,4 +17,7 @@ query($search: String, $first: Int, $last: Int) {
15
17
  }
16
18
  }
17
19
  }
20
+ currentUser {
21
+ ...CurrentUserAttributes
22
+ }
18
23
  }
@@ -1,3 +1,5 @@
1
+ #import "./currentUserAttributes.gql"
2
+
1
3
  query($first: Int, $last: Int) {
2
4
  ovalContents(first: $first, last: $last) {
3
5
  totalCount
@@ -8,4 +10,7 @@ query($first: Int, $last: Int) {
8
10
  originalFilename
9
11
  }
10
12
  }
13
+ currentUser {
14
+ ...CurrentUserAttributes
15
+ }
11
16
  }
@@ -1,3 +1,5 @@
1
+ #import "./currentUserAttributes.gql"
2
+
1
3
  query($first: Int, $last: Int) {
2
4
  ovalPolicies(first: $first, last: $last) {
3
5
  totalCount
@@ -9,4 +11,7 @@ query($first: Int, $last: Int) {
9
11
  }
10
12
  }
11
13
  }
14
+ currentUser {
15
+ ...CurrentUserAttributes
16
+ }
12
17
  }
@@ -1,3 +1,5 @@
1
+ #import "./currentUserAttributes.gql"
2
+
1
3
  query($id: String!) {
2
4
  ovalPolicy(id: $id) {
3
5
  id
@@ -18,4 +20,7 @@ query($id: String!) {
18
20
  }
19
21
  }
20
22
  }
23
+ currentUser {
24
+ ...CurrentUserAttributes
25
+ }
21
26
  }
@@ -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
+ };
@@ -33,6 +33,7 @@ const OvalContentsIndex = props => {
33
33
  resultPath="ovalContents.nodes"
34
34
  pagination={pagination}
35
35
  emptyStateTitle={__('No OVAL Contents found.')}
36
+ permissions={['view_oval_contents']}
36
37
  />
37
38
  </IndexLayout>
38
39
  );
@@ -6,10 +6,18 @@ import withLoading from '../../../components/withLoading';
6
6
  import IndexTable from '../../../components/IndexTable';
7
7
 
8
8
  const OvalContentsTable = props => {
9
- const columns = [{ title: __('Name') }];
9
+ const columns = [
10
+ { title: __('Name') },
11
+ { title: __('URL') },
12
+ { title: __('Original File Name') },
13
+ ];
10
14
 
11
15
  const rows = props.ovalContents.map(ovalContent => ({
12
- cells: [{ title: ovalContent.name }],
16
+ cells: [
17
+ { title: ovalContent.name },
18
+ { title: ovalContent.url || '' },
19
+ { title: ovalContent.originalFilename || '' },
20
+ ],
13
21
  ovalContent,
14
22
  }));
15
23
 
@@ -1,98 +1,112 @@
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 = [
8
- {
9
- request: {
10
- query: ovalContentsQuery,
11
- variables: {
12
- first: 20,
13
- last: 20,
14
- },
12
+ const ovalContents = {
13
+ totalCount: 4,
14
+ nodes: [
15
+ {
16
+ __typename: 'ForemanOpenscap::OvalContent',
17
+ id: 'abc',
18
+ name: 'ansible OVAL content',
19
+ url:
20
+ 'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2',
21
+ originalFilename: '',
15
22
  },
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
- },
23
+ {
24
+ __typename: 'ForemanOpenscap::OvalContent',
25
+ id: 'bcd',
26
+ name: 'dotnet OVAL content',
27
+ url:
28
+ 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
29
+ originalFilename: '',
50
30
  },
51
- },
52
- ];
31
+ {
32
+ __typename: 'ForemanOpenscap::OvalContent',
33
+ id: 'cde',
34
+ name: 'jboss OVAL content',
35
+ url: '',
36
+ originalFilename: 'jboss.oval.xml.bz2',
37
+ },
38
+ {
39
+ __typename: 'ForemanOpenscap::OvalContent',
40
+ id: 'def',
41
+ name: 'openshift OVAL content',
42
+ url: '',
43
+ originalFilename: 'openshift.oval.xml.bz2',
44
+ },
45
+ ],
46
+ };
53
47
 
54
- export const paginatedMocks = [
55
- {
56
- request: {
57
- query: ovalContentsQuery,
58
- variables: {
59
- first: 10,
60
- last: 5,
61
- },
48
+ const paginatedOvalContents = {
49
+ totalCount: 7,
50
+ nodes: [
51
+ {
52
+ __typename: 'ForemanOpenscap::OvalContent',
53
+ id: 'bcd',
54
+ name: 'dotnet OVAL content',
55
+ url:
56
+ 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2',
57
+ originalFilename: '',
62
58
  },
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
- },
59
+ {
60
+ __typename: 'ForemanOpenscap::OvalContent',
61
+ id: 'def',
62
+ name: 'openshift OVAL content',
63
+ url: '',
64
+ originalFilename: 'openshift.oval.xml.bz2',
84
65
  },
66
+ ],
67
+ };
68
+
69
+ const viewer = userFactory('viewer', [
70
+ {
71
+ __typename: 'Permission',
72
+ id: 'MDE6UGVybWlzc2lvbi0yOTY=',
73
+ name: 'view_oval_contents',
85
74
  },
86
- ];
75
+ ]);
76
+
77
+ export const mocks = ovalContentMockFactory(
78
+ { first: 20, last: 20 },
79
+ ovalContents,
80
+ { currentUser: admin }
81
+ );
82
+
83
+ export const paginatedMocks = ovalContentMockFactory(
84
+ { first: 10, last: 5 },
85
+ paginatedOvalContents,
86
+ { currentUser: admin }
87
+ );
87
88
 
88
89
  export const emptyMocks = ovalContentMockFactory(
89
90
  { first: 20, last: 20 },
90
- { totalCount: 0, nodes: [] }
91
+ { totalCount: 0, nodes: [] },
92
+ { currentUser: admin }
91
93
  );
92
94
  export const errorMocks = ovalContentMockFactory(
93
95
  { first: 20, last: 20 },
94
96
  { totalCount: 0, nodes: [] },
95
- [{ message: 'Something very bad happened.' }]
97
+ { errors: [{ message: 'Something very bad happened.' }], currentUser: admin }
98
+ );
99
+
100
+ export const viewerMocks = ovalContentMockFactory(
101
+ { first: 20, last: 20 },
102
+ ovalContents,
103
+ { currentUser: viewer }
104
+ );
105
+
106
+ export const unauthorizedMocks = ovalContentMockFactory(
107
+ { first: 20, last: 20 },
108
+ ovalContents,
109
+ { currentUser: intruder }
96
110
  );
97
111
 
98
112
  export const pushMock = jest.fn();
@@ -16,6 +16,8 @@ import {
16
16
  pagePaginationHistoryMock,
17
17
  emptyMocks,
18
18
  errorMocks,
19
+ viewerMocks,
20
+ unauthorizedMocks,
19
21
  } from './OvalContentsIndex.fixtures';
20
22
 
21
23
  const TestComponent = withMockedProvider(OvalContentsIndex);
@@ -29,7 +31,13 @@ describe('OvalContentsIndex', () => {
29
31
  await waitFor(tick);
30
32
  expect(screen.queryByText('Loading')).not.toBeInTheDocument();
31
33
  expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
34
+ expect(
35
+ screen.getByText(
36
+ 'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2'
37
+ )
38
+ ).toBeInTheDocument();
32
39
  expect(screen.getByText('openshift OVAL content')).toBeInTheDocument();
40
+ expect(screen.getByText('openshift.oval.xml.bz2')).toBeInTheDocument();
33
41
  const pageItems = container.querySelector('.pf-c-pagination__total-items');
34
42
  expect(within(pageItems).getByText(/1 - 4/)).toBeInTheDocument();
35
43
  expect(within(pageItems).getByText('of')).toBeInTheDocument();
@@ -72,4 +80,22 @@ describe('OvalContentsIndex', () => {
72
80
  ).toBeInTheDocument();
73
81
  expect(screen.getByText('Error!')).toBeInTheDocument();
74
82
  });
83
+ it('should load page for user with permissions', async () => {
84
+ render(<TestComponent history={historyMock} mocks={viewerMocks} />);
85
+ await waitFor(tick);
86
+ expect(screen.queryByText('Loading')).not.toBeInTheDocument();
87
+ expect(screen.getByText('ansible OVAL content')).toBeInTheDocument();
88
+ });
89
+ it('should not load page for user without permissions', async () => {
90
+ render(<TestComponent history={historyMock} mocks={unauthorizedMocks} />);
91
+ await waitFor(tick);
92
+ expect(screen.queryByText('Loading')).not.toBeInTheDocument();
93
+ expect(screen.queryByText('ansible OVAL content')).not.toBeInTheDocument();
94
+ expect(
95
+ screen.getByText(
96
+ 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_contents.'
97
+ )
98
+ ).toBeInTheDocument();
99
+ expect(screen.getByText('Permission denied')).toBeInTheDocument();
100
+ });
75
101
  });
@@ -34,6 +34,7 @@ const OvalPoliciesIndex = props => {
34
34
  resultPath="ovalPolicies.nodes"
35
35
  pagination={pagination}
36
36
  emptyStateTitle={__('No OVAL Policies found')}
37
+ permissions={['view_oval_policies']}
37
38
  />
38
39
  </IndexLayout>
39
40
  );
@@ -1,6 +1,11 @@
1
1
  import policiesQuery from '../../../../graphql/queries/ovalPolicies.gql';
2
2
  import { ovalPoliciesPath } 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 policiesMockFactory = mockFactory('ovalPolicies', policiesQuery);
6
11
 
@@ -14,48 +19,77 @@ export const pageParamsHistoryMock = {
14
19
  push: pushMock,
15
20
  };
16
21
 
22
+ const policiesMocks = {
23
+ totalCount: 2,
24
+ nodes: [
25
+ {
26
+ __typename: 'ForemanOpenscap::OvalPolicy',
27
+ id: 'abc',
28
+ name: 'first policy',
29
+ ovalContent: { name: 'first content' },
30
+ },
31
+ {
32
+ __typename: 'ForemanOpenscap::OvalPolicy',
33
+ id: 'xyz',
34
+ name: 'second policy',
35
+ ovalContent: { name: 'second content' },
36
+ },
37
+ ],
38
+ };
39
+
40
+ const pagedPoliciesMocks = {
41
+ totalCount: 7,
42
+ nodes: [
43
+ {
44
+ __typename: 'ForemanOpenscap::OvalPolicy',
45
+ id: 'xyz',
46
+ name: 'sixth policy',
47
+ ovalContent: { name: 'sixth content' },
48
+ },
49
+ {
50
+ __typename: 'ForemanOpenscap::OvalPolicy',
51
+ id: 'abc',
52
+ name: 'seventh policy',
53
+ ovalContent: { name: 'seventh content' },
54
+ },
55
+ ],
56
+ };
57
+
58
+ const viewer = userFactory('viewer', [
59
+ {
60
+ __typename: 'Permission',
61
+ id: 'MDE6UGVybWlzc2lvbi0yOTY=',
62
+ name: 'view_oval_policies',
63
+ },
64
+ ]);
65
+
17
66
  export const mocks = policiesMockFactory(
18
67
  { first: 20, last: 20 },
19
- {
20
- totalCount: 2,
21
- nodes: [
22
- {
23
- id: 'abc',
24
- name: 'first policy',
25
- ovalContent: { name: 'first content' },
26
- },
27
- {
28
- id: 'xyz',
29
- name: 'second policy',
30
- ovalContent: { name: 'second content' },
31
- },
32
- ],
33
- }
68
+ policiesMocks,
69
+ { currentUser: admin }
34
70
  );
35
71
  export const pageParamsMocks = policiesMockFactory(
36
72
  { first: 10, last: 5 },
37
- {
38
- totalCount: 7,
39
- nodes: [
40
- {
41
- id: 'xyz',
42
- name: 'sixth policy',
43
- ovalContent: { name: 'sixth content' },
44
- },
45
- {
46
- id: 'abc',
47
- name: 'seventh policy',
48
- ovalContent: { name: 'seventh content' },
49
- },
50
- ],
51
- }
73
+ pagedPoliciesMocks,
74
+ { currentUser: admin }
52
75
  );
53
76
  export const emptyMocks = policiesMockFactory(
54
77
  { first: 20, last: 20 },
55
- { totalCount: 0, nodes: [] }
78
+ { totalCount: 0, nodes: [] },
79
+ { currentUser: admin }
56
80
  );
57
81
  export const errorMocks = policiesMockFactory(
58
82
  { first: 20, last: 20 },
59
83
  { totalCount: 0, nodes: [] },
60
- [{ message: 'Something very bad happened.' }]
84
+ { errors: [{ message: 'Something very bad happened.' }], currentUser: admin }
85
+ );
86
+ export const viewerMocks = policiesMockFactory(
87
+ { first: 20, last: 20 },
88
+ policiesMocks,
89
+ { currentUser: viewer }
90
+ );
91
+ export const unauthorizedMocks = policiesMockFactory(
92
+ { first: 20, last: 20 },
93
+ policiesMocks,
94
+ { currentUser: intruder }
61
95
  );
@@ -18,6 +18,8 @@ import {
18
18
  pageParamsHistoryMock,
19
19
  emptyMocks,
20
20
  errorMocks,
21
+ viewerMocks,
22
+ unauthorizedMocks,
21
23
  } from './OvalPoliciesIndex.fixtures';
22
24
 
23
25
  import OvalPoliciesIndex from '../OvalPoliciesIndex';
@@ -75,4 +77,22 @@ describe('OvalPoliciesIndex', () => {
75
77
  ).toBeInTheDocument();
76
78
  expect(screen.getByText('Error!')).toBeInTheDocument();
77
79
  });
80
+ it('should load page for user with permissions', async () => {
81
+ render(<TestComponent history={historyMock} mocks={viewerMocks} />);
82
+ await waitFor(tick);
83
+ expect(screen.queryByText('Loading')).not.toBeInTheDocument();
84
+ expect(screen.getByText('first policy')).toBeInTheDocument();
85
+ });
86
+ it('should not load page for user without permissions', async () => {
87
+ render(<TestComponent history={historyMock} mocks={unauthorizedMocks} />);
88
+ await waitFor(tick);
89
+ expect(screen.queryByText('Loading')).not.toBeInTheDocument();
90
+ expect(screen.queryByText('first policy')).not.toBeInTheDocument();
91
+ expect(
92
+ screen.getByText(
93
+ 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_policies.'
94
+ )
95
+ ).toBeInTheDocument();
96
+ expect(screen.getByText('Permission denied')).toBeInTheDocument();
97
+ });
78
98
  });
@@ -36,6 +36,7 @@ const CvesTab = props => {
36
36
  resultPath="cves.nodes"
37
37
  pagination={pagination}
38
38
  emptyStateTitle={__('No CVEs found.')}
39
+ permissions={['view_oval_policies']}
39
40
  />
40
41
  );
41
42
  };
@@ -1,4 +1,4 @@
1
- import { mockFactory } from '../../../../testHelper';
1
+ import { mockFactory, admin, intruder } from '../../../../testHelper';
2
2
  import ovalPolicyQuery from '../../../../graphql/queries/ovalPolicy.gql';
3
3
  import cvesQuery from '../../../../graphql/queries/cves.gql';
4
4
 
@@ -70,9 +70,18 @@ export const historyWithSearch = {
70
70
 
71
71
  export const policyDetailMock = policyDetailMockFactory(
72
72
  { id: ovalPolicy.id },
73
- ovalPolicy
73
+ ovalPolicy,
74
+ { currentUser: admin }
74
75
  );
76
+
77
+ export const policyUnauthorizedMock = policyDetailMockFactory(
78
+ { id: ovalPolicy.id },
79
+ ovalPolicy,
80
+ { currentUser: intruder }
81
+ );
82
+
75
83
  export const policyCvesMock = cvesMockFactory(
76
84
  { search: `oval_policy_id = ${ovalPolicyId}`, first: 5, last: 5 },
77
- cvesResult
85
+ cvesResult,
86
+ { currentUser: admin }
78
87
  );
@@ -20,6 +20,7 @@ import {
20
20
  pushMock,
21
21
  policyCvesMock,
22
22
  ovalPolicyId,
23
+ policyUnauthorizedMock,
23
24
  } from './OvalPoliciesShow.fixtures';
24
25
 
25
26
  const TestComponent = withRouter(withMockedProvider(OvalPoliciesShow));
@@ -67,6 +68,22 @@ describe('OvalPoliciesShow', () => {
67
68
  expect(screen.queryByText('Loading')).not.toBeInTheDocument();
68
69
  expect(screen.getByText('Weekly, on tuesday')).toBeInTheDocument();
69
70
  });
71
+ it('should not load the page when user does not have permissions', async () => {
72
+ render(
73
+ <TestComponent
74
+ history={historyMock}
75
+ match={{ params: { id: ovalPolicyId }, path: ovalPoliciesShowPath }}
76
+ mocks={policyUnauthorizedMock}
77
+ />
78
+ );
79
+ await waitFor(tick);
80
+ expect(screen.queryByText('Loading')).not.toBeInTheDocument();
81
+ expect(
82
+ screen.getByText(
83
+ 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_policies.'
84
+ )
85
+ ).toBeInTheDocument();
86
+ });
70
87
  it('should load CVEs tab when specified in URL', async () => {
71
88
  const mocks = policyDetailMock.concat(policyCvesMock);
72
89
  render(
@@ -24,6 +24,7 @@ const WrappedOvalPoliciesShow = props => {
24
24
  renameData={renameData}
25
25
  resultPath="ovalPolicy"
26
26
  emptyStateTitle={__('No OVAL Policy found')}
27
+ permissions={['view_oval_policies']}
27
28
  />
28
29
  );
29
30
  };
@@ -24,7 +24,7 @@ export const withMockedProvider = Component => props => {
24
24
 
25
25
  return (
26
26
  <ForemanContext.Provider value={ctx}>
27
- <MockedProvider mocks={mocks} addTypename={false}>
27
+ <MockedProvider mocks={mocks}>
28
28
  <Component {...rest} />
29
29
  </MockedProvider>
30
30
  </ForemanContext.Provider>
@@ -40,10 +40,38 @@ export const historyMock = {
40
40
  },
41
41
  };
42
42
 
43
+ export const admin = {
44
+ __typename: 'User',
45
+ id: 'MDE6VXNlci00',
46
+ login: 'admin',
47
+ admin: true,
48
+ permissions: {
49
+ nodes: [],
50
+ },
51
+ };
52
+
53
+ export const userFactory = (login, permissions = []) => ({
54
+ __typename: 'User',
55
+ id: 'MDE6VXNlci01',
56
+ login,
57
+ admin: false,
58
+ permissions: {
59
+ nodes: permissions,
60
+ },
61
+ });
62
+
63
+ export const intruder = userFactory('intruder', [
64
+ {
65
+ __typename: 'Permission',
66
+ id: 'MDE6UGVybWlzc2lvbi0x',
67
+ name: 'view_architectures',
68
+ },
69
+ ]);
70
+
43
71
  export const mockFactory = (resultName, query) => (
44
72
  variables,
45
73
  modelResults,
46
- errors = []
74
+ { errors = [], currentUser = null }
47
75
  ) => {
48
76
  const mock = {
49
77
  request: {
@@ -60,5 +88,9 @@ export const mockFactory = (resultName, query) => (
60
88
  if (errors.length !== 0) {
61
89
  mock.result.errors = errors;
62
90
  }
91
+
92
+ if (currentUser) {
93
+ mock.result.data.currentUser = currentUser;
94
+ }
63
95
  return [mock];
64
96
  };
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_openscap
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.3
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - slukasik@redhat.com
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-13 00:00:00.000000000 Z
11
+ date: 2021-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -430,6 +430,7 @@ files:
430
430
  - webpack/components/RuleSeverity/index.js
431
431
  - webpack/components/withLoading.js
432
432
  - webpack/global_index.js
433
+ - webpack/graphql/queries/currentUserAttributes.gql
433
434
  - webpack/graphql/queries/cves.gql
434
435
  - webpack/graphql/queries/ovalContents.gql
435
436
  - webpack/graphql/queries/ovalPolicies.gql
@@ -438,6 +439,7 @@ files:
438
439
  - webpack/helpers/globalIdHelper.js
439
440
  - webpack/helpers/pageParamsHelper.js
440
441
  - webpack/helpers/pathsHelper.js
442
+ - webpack/helpers/permissionsHelper.js
441
443
  - webpack/helpers/tableHelper.js
442
444
  - webpack/index.js
443
445
  - webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js
@@ -501,7 +503,6 @@ test_files:
501
503
  - test/files/tailoring_files/ssg-firefox-ds-tailoring-2.xml
502
504
  - test/files/tailoring_files/ssg-firefox-ds-tailoring.xml
503
505
  - test/files/oval_contents/ansible-2.9.oval.xml.bz2
504
- - test/functional/api/v2/compliance/policies_controller_test.rb
505
506
  - test/functional/api/v2/compliance/scap_content_profiles_controller_test.rb
506
507
  - test/functional/api/v2/compliance/scap_contents_controller_test.rb
507
508
  - test/functional/api/v2/compliance/tailoring_files_controller_test.rb
@@ -509,6 +510,7 @@ test_files:
509
510
  - test/functional/api/v2/compliance/oval_contents_controller_test.rb
510
511
  - test/functional/api/v2/compliance/oval_policies_controller_test.rb
511
512
  - test/functional/api/v2/compliance/oval_reports_controller_test.rb
513
+ - test/functional/api/v2/compliance/policies_controller_test.rb
512
514
  - test/functional/api/v2/hosts_controller_test.rb
513
515
  - test/functional/arf_reports_controller_test.rb
514
516
  - test/functional/openscap_proxies_controller_test.rb
@@ -525,18 +527,18 @@ test_files:
525
527
  - test/unit/openscap_host_test.rb
526
528
  - test/unit/policy_mailer_test.rb
527
529
  - test/unit/scap_content_test.rb
528
- - test/unit/services/config_name_service_test.rb
529
- - test/unit/services/hostgroup_overrider_test.rb
530
- - test/unit/services/lookup_key_overrider_test.rb
531
530
  - test/unit/services/report_dashboard/data_test.rb
532
531
  - test/unit/services/tailoring_files_proxy_check_test.rb
533
532
  - test/unit/services/oval/cves_test.rb
534
533
  - test/unit/services/oval/setup_test.rb
534
+ - test/unit/services/config_name_service_test.rb
535
+ - test/unit/services/hostgroup_overrider_test.rb
536
+ - test/unit/services/lookup_key_overrider_test.rb
535
537
  - test/unit/tailoring_file_test.rb
536
- - test/unit/policy_test.rb
537
538
  - test/unit/oval_host_test.rb
538
539
  - test/unit/oval_policy_test.rb
539
540
  - test/unit/oval_status_test.rb
541
+ - test/unit/policy_test.rb
540
542
  - test/fixtures/cve_fixtures.rb
541
543
  - test/graphql/queries/oval_contents_query_test.rb
542
544
  - test/graphql/queries/oval_policies_query_test.rb