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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/compliance/arf_reports_controller.rb +0 -6
- data/app/helpers/arf_report_dashboard_helper.rb +2 -4
- data/app/helpers/compliance_hosts_helper.rb +1 -1
- data/app/helpers/policies_helper.rb +1 -1
- data/app/services/foreman_openscap/client_config/base.rb +1 -0
- data/app/services/foreman_openscap/client_config/puppet.rb +6 -2
- data/app/views/arf_reports/_metrics.html.erb +4 -4
- data/app/views/compliance_hosts/show.html.erb +4 -6
- data/app/views/dashboard/_compliance_reports_breakdown_widget.html.erb +4 -3
- data/app/views/policy_dashboard/_policy_chart_widget.html.erb +3 -2
- data/db/migrate/20200117135424_migrate_port_overrides_to_int.rb +2 -1
- data/db/migrate/20201202110213_update_puppet_port_param_type.rb +2 -1
- data/lib/foreman_openscap/engine.rb +0 -7
- data/lib/foreman_openscap/version.rb +1 -1
- data/test/functional/api/v2/compliance/oval_reports_controller_test.rb +1 -1
- data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -0
- data/test/helpers/arf_report_dashboard_helper_test.rb +9 -10
- data/test/helpers/policy_dashboard_helper_test.rb +1 -1
- data/test/test_plugin_helper.rb +9 -4
- data/test/unit/policy_test.rb +1 -1
- data/test/unit/services/config_name_service_test.rb +1 -0
- data/test/unit/services/hostgroup_overrider_test.rb +2 -1
- data/test/unit/services/lookup_key_overrider_test.rb +4 -1
- data/webpack/components/EmptyState.js +9 -3
- data/webpack/components/withLoading.js +20 -1
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/cves.gql +5 -0
- data/webpack/graphql/queries/ovalContents.gql +5 -0
- data/webpack/graphql/queries/ovalPolicies.gql +5 -0
- data/webpack/graphql/queries/ovalPolicy.gql +5 -0
- data/webpack/helpers/permissionsHelper.js +42 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +1 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +10 -2
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +91 -77
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +26 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +1 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +67 -33
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +20 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +1 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +12 -3
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +17 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +1 -0
- data/webpack/testHelper.js +34 -2
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 534b669988c89b1335565f67c02b63443b0cfd36636749397b50617c4797316b
|
4
|
+
data.tar.gz: 4ee455ef101cc0bb6b034f1b081e2d604848620e7438e3ae887493ee77d13015
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2ddaa5f34a78a9f085f5564f7e12f28071baf8b1679e645c327bb6d229986f0b399ae9a3bf50078e7d4c536014b43df974125851277ff1584130918b93382a2
|
7
|
+
data.tar.gz: 6a115b58c74b88f4c9e4f81ab5c31a0db2195a285d2c2eb4612251aead4131a426b34aab2593b7e8b6e2b656fee92cae0adc1b3d5bd19787466514e35312fd70
|
@@ -6,11 +6,9 @@ module ArfReportDashboardHelper
|
|
6
6
|
}.freeze
|
7
7
|
|
8
8
|
def breakdown_chart_data(categories, report, colors = COLORS)
|
9
|
-
|
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
|
-
}
|
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' }
|
19
|
+
{ :data => data, :xAxisDataLabel => 'dates', :config => 'timeseries' }
|
20
20
|
end
|
21
21
|
|
22
22
|
def compliance_host_multiple_actions
|
@@ -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?(
|
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
|
8
|
-
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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
|
3
|
-
<% report = ForemanOpenscap::ReportDashboard::Data.new().report %>
|
4
|
-
<%=
|
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
|
6
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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
|
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 =
|
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
|
-
|
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 =
|
26
|
-
assert_equal "Number of Events", res[
|
27
|
-
assert_equal "Rule Results", res[
|
28
|
-
assert_equal 3, res[
|
29
|
-
assert_include res[
|
30
|
-
assert_include res[
|
31
|
-
assert_include res[
|
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 =
|
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]]
|
data/test/test_plugin_helper.rb
CHANGED
@@ -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
|
|
data/test/unit/policy_test.rb
CHANGED
@@ -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
|
-
|
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;
|
@@ -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
|
}
|
@@ -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
|
+
};
|
@@ -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 = [
|
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: [
|
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 {
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
});
|
@@ -1,6 +1,11 @@
|
|
1
1
|
import policiesQuery from '../../../../graphql/queries/ovalPolicies.gql';
|
2
2
|
import { ovalPoliciesPath } from '../../../../helpers/pathsHelper';
|
3
|
-
import {
|
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
|
-
|
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
|
-
|
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
|
});
|
@@ -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(
|
data/webpack/testHelper.js
CHANGED
@@ -24,7 +24,7 @@ export const withMockedProvider = Component => props => {
|
|
24
24
|
|
25
25
|
return (
|
26
26
|
<ForemanContext.Provider value={ctx}>
|
27
|
-
<MockedProvider mocks={mocks}
|
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
|
+
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-
|
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
|