foreman_openscap 0.4.3 → 0.5.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/assets/javascripts/foreman_openscap/load_report.js +1 -1
- data/app/assets/javascripts/foreman_openscap/policy_edit.js +0 -2
- data/app/controllers/api/v2/compliance/arf_reports_controller.rb +7 -22
- data/app/controllers/api/v2/compliance/policies_controller.rb +5 -5
- data/app/controllers/api/v2/compliance/scap_contents_controller.rb +3 -3
- data/app/controllers/arf_reports_controller.rb +62 -0
- data/app/controllers/{scaptimony_dashboard_controller.rb → compliance_dashboard_controller.rb} +1 -1
- data/app/controllers/compliance_hosts_controller.rb +5 -0
- data/app/controllers/{scaptimony_policies_controller.rb → policies_controller.rb} +13 -13
- data/app/controllers/{scaptimony_policy_dashboard_controller.rb → policy_dashboard_controller.rb} +3 -3
- data/app/controllers/{scaptimony_scap_contents_controller.rb → scap_contents_controller.rb} +11 -11
- data/app/helpers/{scaptimony_report_dashboard_helper.rb → arf_report_dashboard_helper.rb} +3 -5
- data/app/helpers/arf_reports_helper.rb +21 -0
- data/app/helpers/compliance_hosts_helper.rb +25 -0
- data/app/helpers/concerns/foreman_openscap/hosts_helper_extensions.rb +2 -32
- data/app/helpers/{scaptimony_policies_helper.rb → policies_helper.rb} +6 -2
- data/app/helpers/{scaptimony_policy_dashboard_helper.rb → policy_dashboard_helper.rb} +8 -8
- data/app/lib/proxy_api/available_proxy.rb +26 -0
- data/app/lib/proxy_api/openscap.rb +40 -0
- data/app/mailers/foreman_openscap/policy_mailer.rb +42 -0
- data/app/models/concerns/foreman_openscap/compliance_status_scoped_search.rb +91 -0
- data/app/models/concerns/foreman_openscap/host_extensions.rb +73 -17
- data/app/models/concerns/foreman_openscap/hostgroup_extensions.rb +3 -5
- data/app/models/foreman_openscap/arf_report.rb +165 -0
- data/app/models/foreman_openscap/asset.rb +27 -0
- data/app/models/foreman_openscap/asset_policy.rb +6 -0
- data/app/models/foreman_openscap/compliance_status.rb +50 -0
- data/app/models/{concerns/foreman_openscap/policy_extensions.rb → foreman_openscap/policy.rb} +72 -45
- data/app/models/foreman_openscap/policy_arf_report.rb +8 -0
- data/app/models/foreman_openscap/policy_revision.rb +6 -0
- data/app/models/foreman_openscap/scap_content.rb +112 -0
- data/app/models/foreman_openscap/scap_content_profile.rb +6 -0
- data/app/overrides/hosts/overview/host_compliance_status.rb +4 -4
- data/app/services/foreman_openscap/arf_report_status_calculator.rb +45 -0
- data/app/services/{scaptimony → foreman_openscap}/host_report_dashboard/data.rb +12 -6
- data/app/services/{scaptimony → foreman_openscap}/policy_dashboard/data.rb +5 -5
- data/app/services/{scaptimony → foreman_openscap}/report_dashboard/data.rb +4 -4
- data/app/views/api/v2/compliance/policies/create.json.rabl +3 -0
- data/app/views/{scaptimony_arf_reports → arf_reports}/_list.html.erb +4 -4
- data/app/views/arf_reports/_metrics.html.erb +37 -0
- data/app/views/arf_reports/_output.html.erb +23 -0
- data/app/views/{scaptimony_arf_reports → arf_reports}/index.html.erb +0 -0
- data/app/views/arf_reports/show.html.erb +14 -0
- data/app/views/{scaptimony_arf_reports/show.html.erb → arf_reports/show_html.html.erb} +2 -3
- data/app/views/compliance_hosts/_compliance_status.erb +6 -0
- data/app/views/{scaptimony_hosts → compliance_hosts}/show.html.erb +9 -2
- data/app/views/dashboard/{_foreman_openscap_host_reports_widget.html.erb → _compliance_host_reports_widget.html.erb} +3 -3
- data/app/views/dashboard/{_foreman_openscap_reports_breakdown_widget.html.erb → _compliance_reports_breakdown_widget.html.erb} +1 -1
- data/app/views/foreman_openscap/policy_mailer/_dashboard.erb +21 -0
- data/app/views/foreman_openscap/policy_mailer/_hosts.erb +44 -0
- data/app/views/foreman_openscap/policy_mailer/_list.erb +10 -0
- data/app/views/foreman_openscap/policy_mailer/_policy.erb +7 -0
- data/app/views/foreman_openscap/policy_mailer/policy_summary.erb +19 -0
- data/app/views/{scaptimony_policies → policies}/_form.html.erb +2 -8
- data/app/views/{scaptimony_policies → policies}/_list.html.erb +5 -5
- data/app/views/policies/_scap_content_results.html.erb +3 -0
- data/app/views/policies/create.html.erb +2 -0
- data/app/views/{scaptimony_policies → policies}/disassociate_multiple_hosts.html.erb +2 -2
- data/app/views/{scaptimony_policies → policies}/edit.html.erb +0 -0
- data/app/views/{scaptimony_policies → policies}/index.html.erb +1 -1
- data/app/views/policies/new.html.erb +2 -0
- data/app/views/{scaptimony_policies → policies}/select_multiple_hosts.html.erb +2 -2
- data/app/views/{scaptimony_policies → policies}/show.html.erb +1 -1
- data/app/views/{scaptimony_policies → policies}/steps/_create_policy_form.html.erb +0 -0
- data/app/views/{scaptimony_policies → policies}/steps/_hostgroups_form.html.erb +0 -0
- data/app/views/{scaptimony_policies → policies}/steps/_locations_form.html.erb +0 -0
- data/app/views/{scaptimony_policies → policies}/steps/_organizations_form.html.erb +0 -0
- data/app/views/policies/steps/_scap_content_form.html.erb +9 -0
- data/app/views/{scaptimony_policies → policies}/steps/_schedule_form.html.erb +1 -1
- data/app/views/{scaptimony_policies → policies}/steps/_step_form.html.erb +3 -3
- data/app/views/{scaptimony_policies → policies}/welcome.html.erb +2 -2
- data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_chart_widget.html.erb +0 -0
- data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_reports.html.erb +2 -2
- data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_status_widget.html.erb +3 -3
- data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/index.html.erb +0 -0
- data/app/views/{scaptimony_scap_contents → scap_contents}/_form.html.erb +5 -6
- data/app/views/{scaptimony_scap_contents → scap_contents}/_list.html.erb +3 -3
- data/app/views/{scaptimony_scap_contents → scap_contents}/edit.html.erb +0 -0
- data/app/views/{scaptimony_scap_contents → scap_contents}/index.html.erb +1 -1
- data/app/views/{scaptimony_scap_contents → scap_contents}/new.html.erb +0 -0
- data/app/views/{scaptimony_scap_contents → scap_contents}/welcome.html.erb +2 -2
- data/config/routes.rb +15 -11
- data/db/migrate/20141013172051_create_scaptimony_policies.rb +9 -0
- data/db/migrate/20141014105333_create_scaptimony_assets.rb +10 -0
- data/db/migrate/20141015092642_create_scaptimony_arf_reports.rb +13 -0
- data/db/migrate/20141015115511_add_arf_report_unique_constraint.rb +6 -0
- data/db/migrate/20141104164201_create_scaptimony_scap_contents.rb +7 -0
- data/db/migrate/20141104171545_create_scaptimony_policy_revisions.rb +14 -0
- data/db/migrate/20141105174625_add_description_to_scaptimony_policy_revisions.rb +5 -0
- data/db/migrate/20141105174834_add_columns_to_scaptimony_policies.rb +12 -0
- data/db/migrate/20141107091756_add_columns_to_scaptimony_scap_contents.rb +8 -0
- data/db/migrate/20141111104519_add_constraint_to_scaptimony_scap_contents.rb +5 -0
- data/db/migrate/20141113221054_create_scaptimony_scap_content_profiles.rb +12 -0
- data/db/migrate/20141116170632_remove_xccdf_profile_from_scaptimony_policies.rb +5 -0
- data/db/migrate/20141116171305_add_profile_to_scaptimony_policies.rb +6 -0
- data/db/migrate/20141118142954_add_constraint_to_scaptimony_policies.rb +5 -0
- data/db/migrate/20141119164918_create_scaptimony_xccdf_results.rb +8 -0
- data/db/migrate/20141119175434_create_scaptimony_xccdf_rules.rb +8 -0
- data/db/migrate/20141119182606_create_scaptimony_xccdf_rule_results.rb +9 -0
- data/db/migrate/20141121120326_create_scaptimony_arf_report_breakdowns.rb +24 -0
- data/db/migrate/20141121164042_replace_arf_report_breakdown_view.rb +25 -0
- data/db/migrate/20141206211151_create_scaptimony_assets_policies.rb +9 -0
- data/db/migrate/20141214112917_add_scap_file_to_scap_content.rb +5 -0
- data/db/migrate/20141216154502_rename_scaptimony_asset_policies.rb +5 -0
- data/db/migrate/20150111085317_polymorph_asset.rb +8 -0
- data/db/migrate/20150112152944_create_scaptimony_arf_report_raws.rb +10 -0
- data/db/migrate/20150114210634_rename_scaptimony_arf_report_raw_raw.rb +5 -0
- data/db/migrate/20150115155947_add_scaptimony_scap_content_digest.rb +21 -0
- data/db/migrate/20150116083129_add_day_of_month_and_cron_line_to_scaptimony_policy.rb +6 -0
- data/db/migrate/20150821100137_migrate_from_scaptimony.rb +59 -0
- data/db/migrate/20150827123826_remove_scaptimony_permissions.rb +21 -0
- data/db/migrate/20150925124959_create_policy_arf_reports.rb +13 -0
- data/db/migrate/20150929124853_add_result_to_logs.rb +9 -0
- data/db/migrate/20150929152345_move_arf_reports_to_reports_table.rb +179 -0
- data/db/migrate/20151023131950_link_arf_report_directly_to_host.rb +17 -0
- data/db/seeds.d/openscap_policy_notification.rb +9 -0
- data/lib/foreman_openscap/bulk_upload.rb +3 -1
- data/lib/foreman_openscap/engine.rb +53 -42
- data/lib/foreman_openscap/helper.rb +8 -0
- data/lib/foreman_openscap/version.rb +1 -1
- data/lib/tasks/foreman_openscap_tasks.rake +14 -0
- data/test/factories/arf_report_factory.rb +9 -6
- data/test/factories/asset_factory.rb +1 -1
- data/test/factories/compliance_host_factory.rb +9 -0
- data/test/factories/compliance_log_factory.rb +11 -0
- data/test/factories/policy_arf_report_factory.rb +6 -0
- data/test/factories/policy_factory.rb +3 -2
- data/test/factories/scap_content_related.rb +2 -2
- data/test/functional/api/v2/compliance/arf_reports_controller_test.rb +4 -3
- data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -2
- data/test/functional/api/v2/compliance/scap_contents_controller_test.rb +3 -1
- data/test/lib/foreman_openscap/bulk_upload_test.rb +1 -1
- data/test/test_plugin_helper.rb +30 -0
- data/test/unit/arf_report_status_calculator_test.rb +11 -0
- data/test/unit/arf_report_test.rb +148 -0
- data/test/unit/compliance_status_test.rb +71 -0
- data/test/unit/openscap_host_test.rb +38 -7
- data/test/unit/policy_mailer_test.rb +38 -0
- data/test/unit/scap_content_test.rb +32 -0
- metadata +130 -74
- data/app/controllers/scaptimony_arf_reports_controller.rb +0 -34
- data/app/controllers/scaptimony_hosts_controller.rb +0 -5
- data/app/models/concerns/foreman_openscap/arf_report_extensions.rb +0 -50
- data/app/models/concerns/foreman_openscap/asset_extensions.rb +0 -34
- data/app/models/concerns/foreman_openscap/scap_content_extensions.rb +0 -40
- data/app/overrides/hosts/index/host_arf_report.rb +0 -5
- data/app/views/scaptimony_arf_reports/_host_report.html.erb +0 -8
- data/app/views/scaptimony_hosts/_host_status.html.erb +0 -17
- data/app/views/scaptimony_policies/_scap_content_results.html.erb +0 -7
- data/app/views/scaptimony_policies/create.html.erb +0 -2
- data/app/views/scaptimony_policies/new.html.erb +0 -2
- data/app/views/scaptimony_policies/steps/_scap_content_form.html.erb +0 -17
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
require 'scaptimony/asset'
|
|
2
|
-
|
|
3
1
|
module ForemanOpenscap
|
|
4
2
|
module HostgroupExtensions
|
|
5
3
|
extend ActiveSupport::Concern
|
|
6
4
|
|
|
7
5
|
included do
|
|
8
|
-
has_one :asset, :as => :assetable, :class_name => "::
|
|
9
|
-
has_many :asset_policies, :through => :asset, :class_name => "::
|
|
10
|
-
has_many :policies, :through => :asset_policies, :class_name => "::
|
|
6
|
+
has_one :asset, :as => :assetable, :class_name => "::ForemanOpenscap::Asset"
|
|
7
|
+
has_many :asset_policies, :through => :asset, :class_name => "::ForemanOpenscap::AssetPolicy"
|
|
8
|
+
has_many :policies, :through => :asset_policies, :class_name => "::ForemanOpenscap::Policy"
|
|
11
9
|
end
|
|
12
10
|
end
|
|
13
11
|
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'foreman_openscap/helper'
|
|
2
|
+
|
|
3
|
+
module ForemanOpenscap
|
|
4
|
+
class ArfReport < ::Report
|
|
5
|
+
include Taxonomix
|
|
6
|
+
|
|
7
|
+
RESULT = %w(pass fail error unknown notapplicable notchecked notselected informational fixed)
|
|
8
|
+
METRIC = %w(passed othered failed)
|
|
9
|
+
BIT_NUM = 10
|
|
10
|
+
MAX = (1 << BIT_NUM) - 1
|
|
11
|
+
|
|
12
|
+
has_one :policy_arf_report, :dependent => :destroy
|
|
13
|
+
has_one :policy, :through => :policy_arf_report
|
|
14
|
+
has_one :asset, :through => :host, :class_name => 'ForemanOpenscap::Asset'
|
|
15
|
+
after_save :assign_locations_organizations
|
|
16
|
+
validate :result, :inclusion => { :in => RESULT }
|
|
17
|
+
|
|
18
|
+
default_scope {
|
|
19
|
+
with_taxonomy_scope do
|
|
20
|
+
order("#{self.table_name}.created_at DESC")
|
|
21
|
+
end
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
scope :hosts, lambda { includes(:policy) }
|
|
25
|
+
scope :of_policy, lambda { |policy_id| joins(:policy_arf_report).merge(PolicyArfReport.of_policy(policy_id)) }
|
|
26
|
+
|
|
27
|
+
scope :latest,
|
|
28
|
+
joins('INNER JOIN (SELECT host_id, policy_id, max(reports.id) AS id
|
|
29
|
+
FROM reports INNER JOIN foreman_openscap_policy_arf_reports
|
|
30
|
+
ON reports.id = foreman_openscap_policy_arf_reports.arf_report_id
|
|
31
|
+
GROUP BY host_id, policy_id) latest
|
|
32
|
+
ON reports.id = latest.id')
|
|
33
|
+
|
|
34
|
+
scope :latest_of_policy, lambda { |policy|
|
|
35
|
+
joins("INNER JOIN (SELECT host_id, policy_id, max(reports.id) AS id
|
|
36
|
+
FROM reports INNER JOIN foreman_openscap_policy_arf_reports
|
|
37
|
+
ON reports.id = foreman_openscap_policy_arf_reports.arf_report_id
|
|
38
|
+
WHERE policy_id = #{policy.id}
|
|
39
|
+
GROUP BY host_id, policy_id) latest
|
|
40
|
+
ON reports.id = latest.id")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
scope :failed, lambda { where("(#{report_status_column} >> #{ bit_mask 'failed' }) > 0") }
|
|
44
|
+
scope :not_failed, lambda { where("(#{report_status_column} >> #{ bit_mask 'failed' }) = 0") }
|
|
45
|
+
|
|
46
|
+
scope :othered, lambda { where("(#{report_status_column} >> #{ bit_mask 'othered' }) > 0").merge(not_failed) }
|
|
47
|
+
scope :not_othered, lambda { where("(#{report_status_column} >> #{ bit_mask 'othered' }) = 0") }
|
|
48
|
+
|
|
49
|
+
scope :passed, lambda { where("(#{report_status_column} >> #{ bit_mask 'passed' }) > 0").merge(not_failed).merge(not_othered) }
|
|
50
|
+
|
|
51
|
+
def self.bit_mask(status)
|
|
52
|
+
ComplianceStatus.bit_mask(status)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.report_status_column
|
|
56
|
+
"status"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def status=(st)
|
|
60
|
+
s = case st
|
|
61
|
+
when Integer, Fixnum
|
|
62
|
+
st
|
|
63
|
+
when Hash
|
|
64
|
+
ArfReportStatusCalculator.new(:counters => st).calculate
|
|
65
|
+
else
|
|
66
|
+
fail Foreman::Exception(N_('Unsupported report status format'))
|
|
67
|
+
end
|
|
68
|
+
write_attribute(:status, s)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
delegate :status, :status_of, :to => :calculator
|
|
72
|
+
delegate(*METRIC, :to => :calculator)
|
|
73
|
+
|
|
74
|
+
def calculator
|
|
75
|
+
ArfReportStatusCalculator.new(:bit_field => read_attribute(self.class.report_status_column))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def passed
|
|
79
|
+
status_of "passed"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def failed
|
|
83
|
+
status_of "failed"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def othered
|
|
87
|
+
status_of "othered"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def rules_count
|
|
91
|
+
status.values.sum
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.create_arf(asset, params)
|
|
95
|
+
# fail if policy does not exist.
|
|
96
|
+
arf_report = nil
|
|
97
|
+
policy = Policy.find(params[:policy_id])
|
|
98
|
+
ArfReport.transaction do
|
|
99
|
+
# TODO:RAILS-4.0: This should become arf_report = ArfReport.find_or_create_by! ...
|
|
100
|
+
arf_report = ArfReport.create!(:host_id => asset.host.id,
|
|
101
|
+
:reported_at => Time.at(params[:date].to_i),
|
|
102
|
+
:status => params[:metrics],
|
|
103
|
+
:metrics => params[:metrics])
|
|
104
|
+
PolicyArfReport.where(:arf_report_id => arf_report.id, :policy_id => policy.id, :digest => params[:digest]).first_or_create!
|
|
105
|
+
if params[:logs]
|
|
106
|
+
params[:logs].each do |log|
|
|
107
|
+
src = Source.find_or_create(log[:source])
|
|
108
|
+
msg = Message.find_or_create(N_(log[:title]))
|
|
109
|
+
#TODO: log level
|
|
110
|
+
Log.create!(:source_id => src.id,
|
|
111
|
+
:message_id => msg.id,
|
|
112
|
+
:level_id => 1,
|
|
113
|
+
:result => log[:result],
|
|
114
|
+
:report_id => arf_report.id)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
arf_report
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def assign_locations_organizations
|
|
122
|
+
if host
|
|
123
|
+
self.location_ids = [host.location_id] if SETTINGS[:locations_enabled]
|
|
124
|
+
self.organization_ids = [host.organization_id] if SETTINGS[:organizations_enabled]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def failed?
|
|
129
|
+
failed > 0
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def passed?
|
|
133
|
+
passed > 0 && failed == 0 && othered == 0
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def othered?
|
|
137
|
+
!passed? && !failed?
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def to_html
|
|
141
|
+
proxy.arf_report_html(self, ForemanOpenscap::Helper::find_name_or_uuid_by_host(host))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def to_bzip
|
|
145
|
+
proxy.arf_report_bzip(self, ForemanOpenscap::Helper::find_name_or_uuid_by_host(host))
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def equal?(other)
|
|
149
|
+
results = [logs, other.logs].flatten.group_by(&:source_id).values
|
|
150
|
+
# for each rule, there should be one result from both reports
|
|
151
|
+
return false unless results.map(&:length).all? { |item| item == 2 }
|
|
152
|
+
results.all? { |result| result.first.source_id == result.last.source_id } &&
|
|
153
|
+
host_id == other.host_id &&
|
|
154
|
+
policy.id == other.policy.id
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def proxy
|
|
158
|
+
return @proxy if @proxy
|
|
159
|
+
scap_class = host.info['classes']['foreman_scap_client']
|
|
160
|
+
port = scap_class['port']
|
|
161
|
+
server = scap_class['server']
|
|
162
|
+
@proxy = ::ProxyAPI::Openscap.new(:url => "https://#{server}:#{port}")
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ForemanOpenscap
|
|
2
|
+
class Asset < ActiveRecord::Base
|
|
3
|
+
has_many :asset_policies
|
|
4
|
+
has_many :policies, :through => :asset_policies
|
|
5
|
+
belongs_to :assetable, :polymorphic => true
|
|
6
|
+
|
|
7
|
+
scope :hosts, where(:assetable_type => 'Host::Base')
|
|
8
|
+
|
|
9
|
+
def host
|
|
10
|
+
fetch_asset('Host::Base')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def hostgroup
|
|
14
|
+
fetch_asset('Hostgroup')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
assetable.name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def fetch_asset(type)
|
|
24
|
+
assetable if assetable_type == type
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module ForemanOpenscap
|
|
2
|
+
class ComplianceStatus < ::HostStatus::Status
|
|
3
|
+
COMPLIANT = 0
|
|
4
|
+
INCONCLUSIVE = 1
|
|
5
|
+
INCOMPLIANT = 2
|
|
6
|
+
|
|
7
|
+
def self.status_name
|
|
8
|
+
N_('Compliance')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.bit_mask(status)
|
|
12
|
+
"#{ArfReport::BIT_NUM * ArfReport::METRIC.index(status)} & #{ArfReport::MAX}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_label(options = {})
|
|
16
|
+
case to_status
|
|
17
|
+
when COMPLIANT
|
|
18
|
+
N_('Compliant')
|
|
19
|
+
when INCONCLUSIVE
|
|
20
|
+
N_('Inconclusive')
|
|
21
|
+
when INCOMPLIANT
|
|
22
|
+
N_('Incompliant')
|
|
23
|
+
else
|
|
24
|
+
N_('Unknown Compliance status')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_global(options = {})
|
|
29
|
+
case to_status
|
|
30
|
+
when COMPLIANT
|
|
31
|
+
::HostStatus::Global::OK
|
|
32
|
+
when INCONCLUSIVE
|
|
33
|
+
::HostStatus::Global::WARN
|
|
34
|
+
else
|
|
35
|
+
::HostStatus::Global::ERROR
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def relevant?
|
|
40
|
+
host.policies.present?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_status(options = {})
|
|
44
|
+
latest_reports = host.policies.map { |p| host.last_report_for_policy p }.flatten
|
|
45
|
+
return INCOMPLIANT if latest_reports.any?(&:failed?)
|
|
46
|
+
return INCONCLUSIVE if latest_reports.any?(&:othered?)
|
|
47
|
+
COMPLIANT
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/app/models/{concerns/foreman_openscap/policy_extensions.rb → foreman_openscap/policy.rb}
RENAMED
|
@@ -1,49 +1,64 @@
|
|
|
1
|
-
#
|
|
2
|
-
# Copyright (c) 2014 Red Hat Inc.
|
|
3
|
-
#
|
|
4
|
-
# This software is licensed to you under the GNU General Public License,
|
|
5
|
-
# version 3 (GPLv3). There is NO WARRANTY for this software, express or
|
|
6
|
-
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
|
|
7
|
-
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv3
|
|
8
|
-
# along with this software; if not, see http://www.gnu.org/licenses/gpl.txt
|
|
9
|
-
#
|
|
10
|
-
|
|
11
1
|
module ForemanOpenscap
|
|
12
|
-
|
|
13
|
-
extend ActiveSupport::Concern
|
|
14
|
-
|
|
2
|
+
class Policy < ActiveRecord::Base
|
|
15
3
|
include Authorizable
|
|
16
4
|
include Taxonomix
|
|
5
|
+
attr_accessible :description, :name, :period, :scap_content_id, :scap_content_profile_id,
|
|
6
|
+
:weekday, :day_of_month, :cron_line, :location_ids, :organization_ids,
|
|
7
|
+
:current_step, :hostgroup_ids
|
|
8
|
+
attr_writer :current_step
|
|
9
|
+
|
|
10
|
+
belongs_to :scap_content
|
|
11
|
+
belongs_to :scap_content_profile
|
|
12
|
+
has_many :policy_arf_reports
|
|
13
|
+
has_many :arf_reports, :through => :policy_arf_reports, :dependent => :destroy
|
|
14
|
+
has_many :asset_policies
|
|
15
|
+
has_many :assets, :through => :asset_policies
|
|
16
|
+
|
|
17
|
+
scoped_search :on => :name, :complete_value => true
|
|
18
|
+
|
|
19
|
+
SCAP_PUPPET_CLASS = 'foreman_scap_client'
|
|
20
|
+
POLICIES_CLASS_PARAMETER = 'policies'
|
|
21
|
+
SERVER_CLASS_PARAMETER = 'server'
|
|
22
|
+
PORT_CLASS_PARAMETER = 'port'
|
|
23
|
+
|
|
24
|
+
validates :name, :presence => true, :uniqueness => true, :format => {without: /\s/}
|
|
25
|
+
validate :ensure_needed_puppetclasses
|
|
26
|
+
validates :period, :inclusion => {:in => %w[weekly monthly custom]},
|
|
27
|
+
:if => Proc.new { |policy| policy.new_record? ? policy.step_index > 3 : !policy.id.blank? }
|
|
28
|
+
validates :weekday, :inclusion => {:in => Date::DAYNAMES.map(&:downcase)},
|
|
29
|
+
:if => Proc.new { |policy| policy.period == 'weekly' && (policy.new_record? ? policy.step_index > 3 : !policy.id.blank?) }
|
|
30
|
+
validates :day_of_month, :numericality => {:greater_than => 0, :less_than => 32},
|
|
31
|
+
:if => Proc.new { |policy| policy.period == 'monthly'&& (policy.new_record? ? policy.step_index > 3 : !policy.id.blank?) }
|
|
32
|
+
validate :valid_cron_line
|
|
33
|
+
validate :ensure_period_specification_present
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
after_save :assign_policy_to_hostgroups
|
|
37
|
+
# before_destroy - ensure that the policy has no hostgroups, or classes
|
|
38
|
+
|
|
39
|
+
default_scope {
|
|
40
|
+
with_taxonomy_scope do
|
|
41
|
+
order("foreman_openscap_policies.name")
|
|
42
|
+
end
|
|
43
|
+
}
|
|
17
44
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
validate :valid_cron_line
|
|
36
|
-
validate :ensure_period_specification_present
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
after_save :assign_policy_to_hostgroups
|
|
40
|
-
# before_destroy - ensure that the policy has no hostgroups, or classes
|
|
41
|
-
|
|
42
|
-
default_scope {
|
|
43
|
-
with_taxonomy_scope do
|
|
44
|
-
order("scaptimony_policies.name")
|
|
45
|
-
end
|
|
46
|
-
}
|
|
45
|
+
def assign_assets(a)
|
|
46
|
+
self.asset_ids = (self.asset_ids + a.collect(&:id)).uniq
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_html
|
|
50
|
+
if scap_content.nil? || scap_content_profile.nil?
|
|
51
|
+
return (_('<h2>Cannot generate HTML guide for %{scap_content}/%{profile}</h2>') %
|
|
52
|
+
{ :scap_content => self.scap_content, :profile => self.scap_content_profile }).html_safe
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if (proxy = scap_content.proxy_url)
|
|
56
|
+
api = ProxyAPI::Openscap.new(:url => proxy)
|
|
57
|
+
else
|
|
58
|
+
return _('<h2>No valid OpenScap proxy server found.</h2>').html_safe
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
api.policy_html_guide(scap_content.scap_file, scap_content_profile.profile_id)
|
|
47
62
|
end
|
|
48
63
|
|
|
49
64
|
def hostgroup_ids
|
|
@@ -66,6 +81,18 @@ module ForemanOpenscap
|
|
|
66
81
|
hostgroup_ids = hostgroups.map(&:id).map(&:to_s)
|
|
67
82
|
end
|
|
68
83
|
|
|
84
|
+
def host_ids
|
|
85
|
+
assets.where(:assetable_type => 'Host::Base').pluck(:assetable_id)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def hosts
|
|
89
|
+
Host.where(:id => host_ids)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def hosts=(hosts)
|
|
93
|
+
host_ids = hosts.map(&:id).map(&:to_s)
|
|
94
|
+
end
|
|
95
|
+
|
|
69
96
|
def steps
|
|
70
97
|
base_steps = ['Create policy', 'SCAP Content', 'Schedule']
|
|
71
98
|
base_steps << 'Locations' if SETTINGS[:locations_enabled]
|
|
@@ -111,13 +138,13 @@ module ForemanOpenscap
|
|
|
111
138
|
|
|
112
139
|
def used_location_ids
|
|
113
140
|
Location.joins(:taxable_taxonomies).where(
|
|
114
|
-
'taxable_taxonomies.taxable_type' => '
|
|
141
|
+
'taxable_taxonomies.taxable_type' => 'ForemanOpenscap::Policy',
|
|
115
142
|
'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
|
|
116
143
|
end
|
|
117
144
|
|
|
118
145
|
def used_organization_ids
|
|
119
146
|
Organization.joins(:taxable_taxonomies).where(
|
|
120
|
-
'taxable_taxonomies.taxable_type' => '
|
|
147
|
+
'taxable_taxonomies.taxable_type' => 'ForemanOpenscap::Policy',
|
|
121
148
|
'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
|
|
122
149
|
end
|
|
123
150
|
|
|
@@ -130,7 +157,7 @@ module ForemanOpenscap
|
|
|
130
157
|
end
|
|
131
158
|
|
|
132
159
|
def unassign_hosts(hosts)
|
|
133
|
-
host_asset_ids =
|
|
160
|
+
host_asset_ids = ForemanOpenscap::Asset.where(:assetable_type => 'Host::Base', :assetable_id => hosts.map(&:id)).pluck(:id)
|
|
134
161
|
self.asset_ids = self.asset_ids - host_asset_ids
|
|
135
162
|
end
|
|
136
163
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'digest/sha2'
|
|
2
|
+
|
|
3
|
+
module ForemanOpenscap
|
|
4
|
+
class DataStreamValidator < ActiveModel::Validator
|
|
5
|
+
def validate(scap_content)
|
|
6
|
+
return unless scap_content.scap_file_changed?
|
|
7
|
+
|
|
8
|
+
unless SmartProxy.with_features('Openscap').any?
|
|
9
|
+
scap_content.errors.add(:base, _('No Proxy with OpenScap features'))
|
|
10
|
+
return false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
if scap_content.proxy_url.nil?
|
|
14
|
+
scap_content.errors.add(:base, _('No Available Proxy to validate SCAP content'))
|
|
15
|
+
return false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
api = ProxyAPI::Openscap.new(:url => scap_content.proxy_url)
|
|
20
|
+
errors = api.validate_scap_content(scap_content.scap_file)
|
|
21
|
+
if errors && errors['errors'].any?
|
|
22
|
+
errors['errors'].each {|error| scap_content.errors.add(:scap_file, _(error))}
|
|
23
|
+
return false
|
|
24
|
+
end
|
|
25
|
+
rescue *ProxyAPI::AvailableProxy::HTTP_ERRORS => e
|
|
26
|
+
scap_content.errors.add(:base, _('No available proxy to validate. Returned with error: %s') % e)
|
|
27
|
+
return false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
unless (scap_content.scap_content_profiles.map(&:profile_id) - scap_content.fetch_profiles.keys).empty?
|
|
32
|
+
scap_content.errors.add(:scap_file, _('Changed file does not include existing SCAP Content profiles'))
|
|
33
|
+
return false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class ScapContent < ActiveRecord::Base
|
|
39
|
+
include Authorizable
|
|
40
|
+
include Taxonomix
|
|
41
|
+
|
|
42
|
+
attr_accessible :original_filename, :scap_file, :title, :location_ids, :organization_ids
|
|
43
|
+
|
|
44
|
+
has_many :scap_content_profiles, :dependent => :destroy
|
|
45
|
+
has_many :policies
|
|
46
|
+
|
|
47
|
+
before_destroy EnsureNotUsedBy.new(:policies)
|
|
48
|
+
|
|
49
|
+
validates_with DataStreamValidator
|
|
50
|
+
validates :title, :presence => true
|
|
51
|
+
validates :digest, :presence => true
|
|
52
|
+
validates :scap_file, :presence => true
|
|
53
|
+
|
|
54
|
+
after_save :create_profiles
|
|
55
|
+
before_validation :redigest, :if => lambda { |scap_content| scap_content.persisted? && scap_content.scap_file_changed? }
|
|
56
|
+
|
|
57
|
+
scoped_search :on => :title, :complete_value => true
|
|
58
|
+
scoped_search :on => :original_filename, :complete_value => true, :rename => :filename
|
|
59
|
+
|
|
60
|
+
default_scope {
|
|
61
|
+
with_taxonomy_scope do
|
|
62
|
+
order("foreman_openscap_scap_contents.title")
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def used_location_ids
|
|
67
|
+
Location.joins(:taxable_taxonomies).where(
|
|
68
|
+
'taxable_taxonomies.taxable_type' => 'ForemanOpenscap::ScapContent',
|
|
69
|
+
'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def used_organization_ids
|
|
73
|
+
Organization.joins(:taxable_taxonomies).where(
|
|
74
|
+
'taxable_taxonomies.taxable_type' => 'ForemanOpenscap::ScapContent',
|
|
75
|
+
'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_label
|
|
79
|
+
title
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def digest
|
|
83
|
+
self[:digest] ||= Digest::SHA256.hexdigest "#{scap_file}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def fetch_profiles
|
|
87
|
+
api = ProxyAPI::Openscap.new(:url => proxy_url)
|
|
88
|
+
profiles = api.fetch_policies_for_scap_content(scap_file)
|
|
89
|
+
profiles
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def proxy_url
|
|
93
|
+
@proxy_url ||= SmartProxy.with_features('Openscap').each do |proxy|
|
|
94
|
+
available = ProxyAPI::AvailableProxy.new(:url => proxy.url)
|
|
95
|
+
break proxy.url if available.available?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def create_profiles
|
|
102
|
+
profiles = fetch_profiles
|
|
103
|
+
profiles.each {|key, title|
|
|
104
|
+
scap_content_profiles.find_or_create_by_profile_id_and_title(key, title)
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def redigest
|
|
109
|
+
self[:digest] = Digest::SHA256.hexdigest "#{scap_file}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|