foreman_openscap 4.3.2 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/compliance/arf_reports_controller.rb +0 -6
- data/app/controllers/api/v2/compliance/oval_policies_controller.rb +1 -1
- data/app/graphql/mutations/oval_contents/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/create.rb +33 -0
- data/app/graphql/mutations/oval_policies/delete.rb +9 -0
- data/app/graphql/mutations/oval_policies/update.rb +15 -0
- data/app/graphql/types/oval_check.rb +11 -0
- data/app/graphql/types/oval_content.rb +2 -0
- data/app/graphql/types/oval_policy.rb +3 -0
- 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 +2 -2
- data/app/models/concerns/foreman_openscap/data_stream_content.rb +1 -1
- data/app/models/concerns/foreman_openscap/host_extensions.rb +0 -6
- data/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +16 -0
- data/app/models/foreman_openscap/arf_report.rb +1 -1
- data/app/models/foreman_openscap/oval_content.rb +2 -0
- data/app/services/foreman_openscap/client_config/base.rb +1 -0
- data/app/services/foreman_openscap/client_config/puppet.rb +6 -2
- data/app/services/foreman_openscap/oval/configure.rb +16 -13
- data/app/services/foreman_openscap/oval/setup.rb +5 -5
- data/app/services/foreman_openscap/oval/setup_check.rb +5 -2
- data/app/views/api/v2/compliance/oval_contents/destroy.json.rabl +3 -0
- 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/db/migrate/20210819143316_drop_unused_tables.rb +6 -0
- data/lib/foreman_openscap/engine.rb +8 -9
- data/lib/foreman_openscap/version.rb +1 -1
- data/package.json +3 -6
- 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/graphql/mutations/oval_policies/delete_mutation_test.rb +63 -0
- data/test/graphql/queries/oval_content_query_test.rb +29 -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/test/unit/services/oval/setup_check_test.rb +37 -0
- data/webpack/components/ConfirmModal.js +63 -0
- data/webpack/components/ConfirmModal.scss +3 -0
- data/webpack/components/EditableInput.js +163 -0
- data/webpack/components/EditableInput.scss +3 -0
- data/webpack/components/EmptyState.js +12 -3
- data/webpack/components/IndexLayout.js +11 -4
- data/webpack/components/IndexTable/index.js +21 -16
- data/webpack/components/LinkButton.js +38 -0
- data/webpack/components/withDeleteModal.js +51 -0
- data/webpack/components/withLoading.js +44 -5
- data/webpack/graphql/mutations/createOvalPolicy.gql +22 -0
- data/webpack/graphql/mutations/deleteOvalContent.gql +9 -0
- data/webpack/graphql/mutations/deleteOvalPolicy.gql +9 -0
- data/webpack/graphql/mutations/updateOvalPolicy.gql +14 -0
- data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
- data/webpack/graphql/queries/cves.gql +5 -0
- data/webpack/graphql/queries/hostgroups.gql +14 -0
- data/webpack/graphql/queries/ovalContent.gql +8 -0
- data/webpack/graphql/queries/ovalContents.gql +8 -0
- data/webpack/graphql/queries/ovalPolicies.gql +8 -0
- data/webpack/graphql/queries/ovalPolicy.gql +8 -0
- data/webpack/helpers/formFieldsHelper.js +113 -0
- data/webpack/helpers/globalIdHelper.js +4 -2
- data/webpack/helpers/mutationHelper.js +68 -0
- data/webpack/helpers/pathsHelper.js +10 -3
- data/webpack/helpers/permissionsHelper.js +42 -0
- data/webpack/helpers/toastHelper.js +3 -0
- data/webpack/helpers/toastsHelper.js +3 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +26 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +50 -5
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +105 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +124 -0
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +98 -77
- data/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +53 -6
- data/webpack/routes/OvalContents/OvalContentsIndex/index.js +7 -1
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +138 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +3 -0
- data/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +73 -0
- data/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +104 -0
- data/webpack/routes/OvalContents/OvalContentsNew/index.js +13 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +62 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +45 -0
- data/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js +0 -0
- data/webpack/routes/OvalContents/OvalContentsShow/index.js +35 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +18 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +34 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +101 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +117 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +71 -21
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +34 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +7 -1
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js +135 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js +119 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js +107 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js +32 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js +147 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js +172 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js +11 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +1 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js +2 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +87 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +49 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +38 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +15 -11
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +80 -2
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +48 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +202 -0
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +50 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +64 -4
- data/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +4 -0
- data/webpack/routes/routes.js +21 -0
- data/webpack/testHelper.js +64 -2
- metadata +63 -7
|
@@ -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)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
module Mutations
|
|
4
|
+
module OvalPolicies
|
|
5
|
+
class DeleteMutationTest < ActiveSupport::TestCase
|
|
6
|
+
let(:policy) { FactoryBot.create(:oval_policy, :oval_content => FactoryBot.create(:oval_content)) }
|
|
7
|
+
let(:policy_id) { Foreman::GlobalId.for(policy) }
|
|
8
|
+
let(:variables) do
|
|
9
|
+
{
|
|
10
|
+
id: policy_id,
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
let(:query) do
|
|
14
|
+
<<-GRAPHQL
|
|
15
|
+
mutation DeleteOvalPolicyMutation($id:ID!){
|
|
16
|
+
deleteOvalPolicy(input:{id:$id}) {
|
|
17
|
+
id
|
|
18
|
+
errors {
|
|
19
|
+
message
|
|
20
|
+
path
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
GRAPHQL
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'with admin user' do
|
|
28
|
+
let(:user) { FactoryBot.create(:user, :admin) }
|
|
29
|
+
|
|
30
|
+
test 'should delete oval policy' do
|
|
31
|
+
context = { current_user: user }
|
|
32
|
+
|
|
33
|
+
policy
|
|
34
|
+
|
|
35
|
+
assert_difference('::ForemanOpenscap::OvalPolicy.count', -1) do
|
|
36
|
+
result = ForemanGraphqlSchema.execute(query, variables: variables, context: context)
|
|
37
|
+
assert_empty result['errors']
|
|
38
|
+
assert_empty result['data']['deleteOvalPolicy']['errors']
|
|
39
|
+
assert_equal policy_id, result['data']['deleteOvalPolicy']['id']
|
|
40
|
+
end
|
|
41
|
+
assert_equal user.id, Audit.last.user_id
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'with user with view permissions' do
|
|
46
|
+
setup do
|
|
47
|
+
policy
|
|
48
|
+
@user = setup_user 'view', 'oval_policies'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'should not delete oval policy' do
|
|
52
|
+
context = { current_user: @user }
|
|
53
|
+
|
|
54
|
+
assert_difference('ForemanOpenscap::OvalPolicy.count', 0) do
|
|
55
|
+
result = ForemanGraphqlSchema.execute(query, variables: variables, context: context)
|
|
56
|
+
assert_not_empty result['errors']
|
|
57
|
+
assert_includes result['errors'].map { |error| error['message'] }.to_sentence, 'Unauthorized.'
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
module Queries
|
|
4
|
+
class OvalContentQueryTest < GraphQLQueryTestCase
|
|
5
|
+
let(:query) do
|
|
6
|
+
<<-GRAPHQL
|
|
7
|
+
query($id:String!) {
|
|
8
|
+
ovalContent(id: $id) {
|
|
9
|
+
id
|
|
10
|
+
name
|
|
11
|
+
originalFilename
|
|
12
|
+
url
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
GRAPHQL
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
let(:oval_content) { FactoryBot.create(:oval_content) }
|
|
19
|
+
|
|
20
|
+
let(:global_id) { Foreman::GlobalId.for(oval_content) }
|
|
21
|
+
let(:variables) { { id: global_id } }
|
|
22
|
+
let(:data) { result['data']['ovalContent'] }
|
|
23
|
+
|
|
24
|
+
test 'should return OVAL Content' do
|
|
25
|
+
assert_equal global_id, data['id']
|
|
26
|
+
assert_equal oval_content.name, data['name']
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -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, :
|
|
17
|
+
hostgroup = FactoryBot.create(:hostgroup, :environment => env, :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
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'test_plugin_helper'
|
|
2
|
+
|
|
3
|
+
class ForemanOpenscap::Oval::SetupCheckTest < ActiveSupport::TestCase
|
|
4
|
+
test 'should show error message with filled in data' do
|
|
5
|
+
check = ::ForemanOpenscap::Oval::SetupCheck.new(
|
|
6
|
+
:id => :test_check,
|
|
7
|
+
:title => _("Will it pass?"),
|
|
8
|
+
:fail_msg => ->(hash) { "There was an error in #{hash[:name]}, you need to #{hash[:action]}" }
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
check.fail_with!(:name => 'your engine', :action => 'run')
|
|
12
|
+
assert_equal 'There was an error in your engine, you need to run', check.fail_msg
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test 'should show error message when it is a string' do
|
|
16
|
+
msg = "Do not panic"
|
|
17
|
+
check = ::ForemanOpenscap::Oval::SetupCheck.new(
|
|
18
|
+
:id => :test_check,
|
|
19
|
+
:title => _("Will it pass?"),
|
|
20
|
+
:fail_msg => msg
|
|
21
|
+
)
|
|
22
|
+
check.fail!
|
|
23
|
+
assert_equal msg, check.fail_msg
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
test 'should not show error message when check not failed' do
|
|
27
|
+
check = ::ForemanOpenscap::Oval::SetupCheck.new(
|
|
28
|
+
:id => :test_check,
|
|
29
|
+
:title => _("Will it pass?"),
|
|
30
|
+
:fail_msg => 'foo'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert_nil check.fail_msg
|
|
34
|
+
check.fail!
|
|
35
|
+
assert_not_nil check.fail_msg
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { Modal, Button, ModalVariant, Spinner } from '@patternfly/react-core';
|
|
4
|
+
|
|
5
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
6
|
+
|
|
7
|
+
import './ConfirmModal.scss';
|
|
8
|
+
|
|
9
|
+
const ConfirmModal = props => {
|
|
10
|
+
const [callMutation, { loading }] = props.prepareMutation();
|
|
11
|
+
|
|
12
|
+
const actions = [
|
|
13
|
+
<Button
|
|
14
|
+
key="confirm"
|
|
15
|
+
variant="primary"
|
|
16
|
+
onClick={() => props.onConfirm(callMutation, props.record.id)}
|
|
17
|
+
isDisabled={loading}
|
|
18
|
+
>
|
|
19
|
+
{__('Confirm')}
|
|
20
|
+
</Button>,
|
|
21
|
+
<Button
|
|
22
|
+
key="cancel"
|
|
23
|
+
variant="link"
|
|
24
|
+
onClick={event => props.onClose()}
|
|
25
|
+
isDisabled={loading}
|
|
26
|
+
>
|
|
27
|
+
{__('Cancel')}
|
|
28
|
+
</Button>,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
if (loading) {
|
|
32
|
+
actions.push(<Spinner key="spinner" size="lg" />);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Modal
|
|
37
|
+
variant={ModalVariant.medium}
|
|
38
|
+
title={props.title}
|
|
39
|
+
isOpen={props.isOpen}
|
|
40
|
+
className="foreman-modal"
|
|
41
|
+
showClose={false}
|
|
42
|
+
actions={actions}
|
|
43
|
+
>
|
|
44
|
+
{props.text}
|
|
45
|
+
</Modal>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
ConfirmModal.propTypes = {
|
|
50
|
+
prepareMutation: PropTypes.func.isRequired,
|
|
51
|
+
onConfirm: PropTypes.func.isRequired,
|
|
52
|
+
record: PropTypes.object,
|
|
53
|
+
onClose: PropTypes.func.isRequired,
|
|
54
|
+
title: PropTypes.string.isRequired,
|
|
55
|
+
isOpen: PropTypes.bool.isRequired,
|
|
56
|
+
text: PropTypes.string.isRequired,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
ConfirmModal.defaultProps = {
|
|
60
|
+
record: null,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default ConfirmModal;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Split,
|
|
7
|
+
SplitItem,
|
|
8
|
+
Spinner,
|
|
9
|
+
FormGroup,
|
|
10
|
+
} from '@patternfly/react-core';
|
|
11
|
+
import {
|
|
12
|
+
TimesIcon,
|
|
13
|
+
CheckIcon,
|
|
14
|
+
PencilAltIcon,
|
|
15
|
+
ExclamationCircleIcon,
|
|
16
|
+
} from '@patternfly/react-icons';
|
|
17
|
+
|
|
18
|
+
import './EditableInput.scss';
|
|
19
|
+
|
|
20
|
+
const EditableInput = props => {
|
|
21
|
+
const [editing, setEditing] = useState(false);
|
|
22
|
+
const [submitting, setSubmitting] = useState(false);
|
|
23
|
+
const [inputValue, setInputValue] = useState(props.value);
|
|
24
|
+
const [error, setError] = useState('');
|
|
25
|
+
const [touched, setTouched] = useState(false);
|
|
26
|
+
|
|
27
|
+
const stopSubmitting = () => setSubmitting(false);
|
|
28
|
+
|
|
29
|
+
const handleSubmit = event => {
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
onSubmit();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const onFinish = () => {
|
|
35
|
+
setSubmitting(false);
|
|
36
|
+
setEditing(false);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const onSubmit = () => {
|
|
40
|
+
setSubmitting(true);
|
|
41
|
+
props.onConfirm(inputValue, onFinish, stopSubmitting, onError);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const onError = err => {
|
|
45
|
+
setTouched(false);
|
|
46
|
+
setError(err);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const onCancel = () => {
|
|
50
|
+
setInputValue(props.value);
|
|
51
|
+
setEditing(false);
|
|
52
|
+
setError('');
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const onChange = value => {
|
|
56
|
+
if (!touched) {
|
|
57
|
+
setTouched(true);
|
|
58
|
+
}
|
|
59
|
+
setInputValue(value);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const editBtn = (
|
|
63
|
+
<SplitItem>
|
|
64
|
+
<Button
|
|
65
|
+
className="inline-edit-icon"
|
|
66
|
+
aria-label={`edit ${props.attrName}`}
|
|
67
|
+
variant="plain"
|
|
68
|
+
onClick={() => setEditing(true)}
|
|
69
|
+
>
|
|
70
|
+
<PencilAltIcon />
|
|
71
|
+
</Button>
|
|
72
|
+
</SplitItem>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (!editing) {
|
|
76
|
+
return (
|
|
77
|
+
<Split>
|
|
78
|
+
<SplitItem>{props.value || <i>{__('None provided')}</i>}</SplitItem>
|
|
79
|
+
{props.allowed && editBtn}
|
|
80
|
+
</Split>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const Component = props.component;
|
|
85
|
+
|
|
86
|
+
const shouldValidate = (isTouched, err) => {
|
|
87
|
+
if (!isTouched) {
|
|
88
|
+
return err ? 'error' : 'success';
|
|
89
|
+
}
|
|
90
|
+
return 'noval';
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const valid = shouldValidate(touched, error);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Split>
|
|
97
|
+
<SplitItem>
|
|
98
|
+
<form onSubmit={handleSubmit} className="pf-c-form">
|
|
99
|
+
<FormGroup
|
|
100
|
+
helperTextInvalid={error}
|
|
101
|
+
helperTextInvalidIcon={<ExclamationCircleIcon />}
|
|
102
|
+
validated={valid}
|
|
103
|
+
>
|
|
104
|
+
<Component
|
|
105
|
+
{...props.inputProps}
|
|
106
|
+
type="text"
|
|
107
|
+
aria-label={`${props.attrName} text input`}
|
|
108
|
+
isDisabled={submitting}
|
|
109
|
+
value={inputValue || ''}
|
|
110
|
+
onChange={onChange}
|
|
111
|
+
validated={valid}
|
|
112
|
+
/>
|
|
113
|
+
</FormGroup>
|
|
114
|
+
</form>
|
|
115
|
+
</SplitItem>
|
|
116
|
+
<SplitItem>
|
|
117
|
+
<Button
|
|
118
|
+
aria-label={`submit ${props.attrName}`}
|
|
119
|
+
variant="plain"
|
|
120
|
+
onClick={onSubmit}
|
|
121
|
+
isDisabled={submitting}
|
|
122
|
+
>
|
|
123
|
+
<CheckIcon />
|
|
124
|
+
</Button>
|
|
125
|
+
</SplitItem>
|
|
126
|
+
<SplitItem>
|
|
127
|
+
<Button
|
|
128
|
+
aria-label={`cancel editing ${props.attrName}`}
|
|
129
|
+
variant="plain"
|
|
130
|
+
onClick={onCancel}
|
|
131
|
+
isDisabled={submitting}
|
|
132
|
+
>
|
|
133
|
+
<TimesIcon />
|
|
134
|
+
</Button>
|
|
135
|
+
</SplitItem>
|
|
136
|
+
<SplitItem>
|
|
137
|
+
{submitting && (
|
|
138
|
+
<Spinner
|
|
139
|
+
key="spinner"
|
|
140
|
+
size="lg"
|
|
141
|
+
id={`edit-${props.attrName}-spinner`}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
</SplitItem>
|
|
145
|
+
</Split>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
EditableInput.propTypes = {
|
|
150
|
+
allowed: PropTypes.bool.isRequired,
|
|
151
|
+
value: PropTypes.string,
|
|
152
|
+
onConfirm: PropTypes.func.isRequired,
|
|
153
|
+
attrName: PropTypes.string.isRequired,
|
|
154
|
+
component: PropTypes.object.isRequired,
|
|
155
|
+
inputProps: PropTypes.object,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
EditableInput.defaultProps = {
|
|
159
|
+
inputProps: {},
|
|
160
|
+
value: '',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export default EditableInput;
|
|
@@ -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,18 +24,20 @@ 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, primaryButton }) => (
|
|
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>
|
|
37
39
|
<EmptyStateBody>{body}</EmptyStateBody>
|
|
40
|
+
{primaryButton}
|
|
38
41
|
</PfEmptyState>
|
|
39
42
|
</Bullseye>
|
|
40
43
|
);
|
|
@@ -42,11 +45,13 @@ const EmptyState = ({ title, body, error, search }) => (
|
|
|
42
45
|
EmptyStateIcon.propTypes = {
|
|
43
46
|
error: PropTypes.bool,
|
|
44
47
|
search: PropTypes.bool,
|
|
48
|
+
lock: PropTypes.bool,
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
EmptyStateIcon.defaultProps = {
|
|
48
52
|
error: false,
|
|
49
53
|
search: false,
|
|
54
|
+
lock: false,
|
|
50
55
|
};
|
|
51
56
|
|
|
52
57
|
EmptyState.propTypes = {
|
|
@@ -54,6 +59,8 @@ EmptyState.propTypes = {
|
|
|
54
59
|
body: PropTypes.string,
|
|
55
60
|
error: PropTypes.oneOfType([PropTypes.shape({}), PropTypes.string]),
|
|
56
61
|
search: PropTypes.bool,
|
|
62
|
+
lock: PropTypes.bool,
|
|
63
|
+
primaryButton: PropTypes.node,
|
|
57
64
|
};
|
|
58
65
|
|
|
59
66
|
EmptyState.defaultProps = {
|
|
@@ -62,6 +69,8 @@ EmptyState.defaultProps = {
|
|
|
62
69
|
'There was an error retrieving data from the server. Check your connection and try again.',
|
|
63
70
|
error: undefined,
|
|
64
71
|
search: false,
|
|
72
|
+
lock: false,
|
|
73
|
+
primaryButton: null,
|
|
65
74
|
};
|
|
66
75
|
|
|
67
76
|
export default EmptyState;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import { Helmet } from 'react-helmet';
|
|
4
|
+
import ToastsList from 'foremanReact/components/ToastsList';
|
|
4
5
|
import {
|
|
5
6
|
Grid,
|
|
6
7
|
GridItem,
|
|
@@ -11,25 +12,31 @@ import {
|
|
|
11
12
|
|
|
12
13
|
import './IndexLayout.scss';
|
|
13
14
|
|
|
14
|
-
const IndexLayout = ({ pageTitle, children }) => (
|
|
15
|
+
const IndexLayout = ({ pageTitle, children, contentWidthSpan }) => (
|
|
15
16
|
<React.Fragment>
|
|
16
17
|
<Helmet>
|
|
17
18
|
<title>{pageTitle}</title>
|
|
18
19
|
</Helmet>
|
|
20
|
+
<ToastsList />
|
|
19
21
|
<Grid className="scap-page-grid">
|
|
20
|
-
<GridItem span={12}>
|
|
22
|
+
<GridItem span={12} className="pf-u-pb-xl">
|
|
21
23
|
<TextContent>
|
|
22
24
|
<Text component={TextVariants.h1}>{pageTitle}</Text>
|
|
23
25
|
</TextContent>
|
|
24
26
|
</GridItem>
|
|
25
|
-
<GridItem span={
|
|
27
|
+
<GridItem span={contentWidthSpan}>{children}</GridItem>
|
|
26
28
|
</Grid>
|
|
27
29
|
</React.Fragment>
|
|
28
30
|
);
|
|
29
31
|
|
|
30
32
|
IndexLayout.propTypes = {
|
|
31
33
|
pageTitle: PropTypes.string.isRequired,
|
|
32
|
-
children: PropTypes.object.isRequired,
|
|
34
|
+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.object]).isRequired,
|
|
35
|
+
contentWidthSpan: PropTypes.number,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
IndexLayout.defaultProps = {
|
|
39
|
+
contentWidthSpan: 12,
|
|
33
40
|
};
|
|
34
41
|
|
|
35
42
|
export default IndexLayout;
|