foreman_openscap 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -6
  3. data/app/assets/javascript/foreman_openscap/period_selector.js +3 -0
  4. data/app/assets/javascript/foreman_openscap/policy_edit.js +37 -0
  5. data/app/assets/stylesheets/foreman_openscap/policy.css.scss +7 -0
  6. data/app/controllers/api/v2/{openscap → compliance}/arf_reports_controller.rb +6 -5
  7. data/app/controllers/scaptimony_arf_reports_controller.rb +9 -6
  8. data/app/controllers/scaptimony_policies_controller.rb +72 -11
  9. data/app/{models/scaptimony/asset.rb → controllers/scaptimony_policy_dashboard_controller.rb} +8 -9
  10. data/app/controllers/scaptimony_scap_contents_controller.rb +25 -16
  11. data/app/helpers/concerns/foreman_openscap/hosts_helper_extensions.rb +3 -4
  12. data/app/helpers/scaptimony_policies_helper.rb +22 -0
  13. data/app/helpers/scaptimony_policy_dashboard_helper.rb +43 -0
  14. data/app/helpers/scaptimony_report_dashboard_helper.rb +20 -0
  15. data/app/models/concerns/foreman_openscap/arf_report_extensions.rb +30 -1
  16. data/app/models/concerns/foreman_openscap/asset_extensions.rb +34 -0
  17. data/app/models/concerns/foreman_openscap/host_extensions.rb +38 -7
  18. data/app/models/concerns/foreman_openscap/policy_extensions.rb +214 -3
  19. data/app/models/concerns/foreman_openscap/scap_content_extensions.rb +21 -1
  20. data/app/overrides/hosts/index/host_arf_report.rb +5 -0
  21. data/app/services/scaptimony/policy_dashboard/data.rb +30 -0
  22. data/app/services/scaptimony/policy_dashboard/loader.rb +20 -0
  23. data/app/services/scaptimony/policy_dashboard/manager.rb +32 -0
  24. data/app/services/scaptimony/report_dashboard/data.rb +37 -0
  25. data/app/views/dashboard/_foreman_openscap_host_reports_widget.html.erb +24 -0
  26. data/app/views/dashboard/_foreman_openscap_reports_breakdown_widget.html.erb +3 -0
  27. data/app/views/scaptimony_arf_reports/_host_report.html.erb +8 -0
  28. data/app/views/scaptimony_arf_reports/_list.html.erb +2 -2
  29. data/app/views/scaptimony_arf_reports/show.html.erb +3 -0
  30. data/app/views/scaptimony_policies/_form.html.erb +23 -4
  31. data/app/views/scaptimony_policies/_list.html.erb +2 -4
  32. data/app/views/scaptimony_policies/create.html.erb +2 -0
  33. data/app/views/scaptimony_policies/edit.html.erb +1 -2
  34. data/app/views/scaptimony_policies/index.html.erb +1 -1
  35. data/app/views/scaptimony_policies/new.html.erb +1 -2
  36. data/app/views/scaptimony_policies/select_multiple_hosts.html.erb +8 -0
  37. data/app/views/scaptimony_policies/show.html.erb +3 -0
  38. data/app/views/scaptimony_policies/steps/_create_policy_form.html.erb +5 -0
  39. data/app/views/scaptimony_policies/steps/_hostgroups_form.html.erb +7 -0
  40. data/app/views/scaptimony_policies/steps/_locations_form.html.erb +13 -0
  41. data/app/views/scaptimony_policies/steps/_organizations_form.html.erb +13 -0
  42. data/app/views/scaptimony_policies/steps/_scap_content_form.html.erb +21 -0
  43. data/app/views/scaptimony_policies/steps/_schedule_form.html.erb +11 -0
  44. data/app/views/scaptimony_policies/steps/_step_form.html.erb +11 -0
  45. data/app/views/scaptimony_policy_dashboard/_policy_chart_widget.html.erb +4 -0
  46. data/app/views/scaptimony_policy_dashboard/_policy_reports.html.erb +24 -0
  47. data/app/views/scaptimony_policy_dashboard/_policy_status_widget.html.erb +12 -0
  48. data/app/views/scaptimony_policy_dashboard/index.html.erb +26 -0
  49. data/app/views/scaptimony_scap_contents/_form.html.erb +8 -0
  50. data/app/views/scaptimony_scap_contents/_list.html.erb +8 -6
  51. data/config/routes.rb +24 -6
  52. data/db/seeds.d/openscap_feature.rb +2 -0
  53. data/lib/foreman_openscap/engine.rb +18 -12
  54. data/lib/foreman_openscap/helper.rb +1 -7
  55. data/lib/foreman_openscap/version.rb +1 -1
  56. metadata +57 -31
  57. data/app/assets/javascript/policy_edit.js +0 -14
  58. data/app/overrides/dashboard/index/sample_override.html.erb.deface +0 -4
  59. data/db/migrate/20141017172055_create_scaptimony_auditable_hosts.rb +0 -9
@@ -0,0 +1,43 @@
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
+ module ScaptimonyPolicyDashboardHelper
12
+ Colors = {
13
+ :compliant_hosts => '#89A54E',
14
+ :incompliant_hosts => '#AA4643',
15
+ :inconclusive_hosts => '#DB843D',
16
+ :report_missing => '#92A8CD',
17
+ }
18
+
19
+ def policy_widget_list
20
+ Scaptimony::PolicyDashboard::Manager.widgets
21
+ end
22
+
23
+ def host_breakdown_chart(report, options = {})
24
+ data = []
25
+ [[:compliant_hosts, _('Compliant hosts')],
26
+ [:incompliant_hosts, _('Incompliant hosts')],
27
+ [:inconclusive_hosts, _('Inconclusive')],
28
+ [:report_missing, _('Not audited')],
29
+ ].each { |i|
30
+ data << {:label => i[1], :data => report[i[0]], :color => Colors[i[0]]}
31
+ }
32
+ flot_pie_chart 'overview', _('Compliance Status'), data, options
33
+ end
34
+
35
+ def status_link(name, label, path)
36
+ content_tag :li do
37
+ content_tag(:i, raw('&nbsp;'), :class=>'label', :style => 'background-color:' + Colors[label]) +
38
+ raw('&nbsp;') +
39
+ link_to(name, path, :class=>'dashboard-links') +
40
+ content_tag(:h4, @report[label])
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,20 @@
1
+ module ScaptimonyReportDashboardHelper
2
+ Colors = {
3
+ :passed => '#89A54E',
4
+ :failed => '#AA4643',
5
+ :othered => '#DB843D',
6
+ }
7
+
8
+
9
+ def reports_breakdown_chart(report, options = {})
10
+ data = []
11
+ [[:failed, _('Failed')],
12
+ [:passed, _('Passed')],
13
+ [:othered, _('Othered')],
14
+ ].each { |i|
15
+ data << {:label => i[1], :data => report[i[0]], :color => Colors[i[0]]}
16
+ }
17
+ flot_pie_chart 'overview', _('Compliance reports breakdown'), data, options
18
+ end
19
+
20
+ end
@@ -13,8 +13,37 @@ require 'scaptimony/arf_report'
13
13
  module ForemanOpenscap
14
14
  module ArfReportExtensions
15
15
  extend ActiveSupport::Concern
16
+ include Taxonomix
16
17
  included do
17
- scoped_search :in => :asset, :on => :name, :complete_value => :true, :rename => "host"
18
+ has_one :host, :through => :asset, :as => :assetable, :source => :assetable, :source_type => 'Host::Base'
19
+
20
+ after_save :assign_locations_organizations
21
+
22
+ scope :hosts, lambda { includes(:policy, :arf_report_breakdown) }
23
+ scope :latest, lambda { includes(:host, :policy, :arf_report_breakdown).limit(5).order("scaptimony_arf_reports.created_at DESC") }
24
+
25
+ scoped_search :in => :host, :on => :name, :complete_value => :true, :rename => "host"
26
+
27
+ default_scope {
28
+ with_taxonomy_scope do
29
+ order("scaptimony_arf_reports.created_at DESC")
30
+ end
31
+ }
32
+ end
33
+
34
+ def assign_locations_organizations
35
+ if host
36
+ self.locations = [host.location]
37
+ self.organizations = [host.organization]
38
+ end
39
+ end
40
+
41
+ def failed?
42
+ failed > 0
43
+ end
44
+
45
+ def passed?
46
+ passed > 0 && !failed?
18
47
  end
19
48
  end
20
49
  end
@@ -0,0 +1,34 @@
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
+ require 'scaptimony/asset'
12
+
13
+ module ForemanOpenscap
14
+ module AssetExtensions
15
+ extend ActiveSupport::Concern
16
+ included do
17
+ belongs_to :assetable, :polymorphic => true
18
+ scope :hosts, where(:assetable_type => 'Host::Base')
19
+ end
20
+
21
+ def host
22
+ fetch_asset('Host::Base')
23
+ end
24
+
25
+ def name
26
+ assetable.name
27
+ end
28
+
29
+ private
30
+ def fetch_asset(type)
31
+ assetable if assetable_type == type
32
+ end
33
+ end
34
+ end
@@ -3,21 +3,52 @@ require 'scaptimony/asset'
3
3
  module ForemanOpenscap
4
4
  module HostExtensions
5
5
  extend ActiveSupport::Concern
6
+ ::Host::Managed::Jail.allow :policies_enc
6
7
 
7
8
  included do
8
- has_one :auditable_host, :class_name => "::Scaptimony::AuditableHost",
9
- :foreign_key => :host_id, :inverse_of => :host
9
+ has_one :asset, :as => :assetable, :class_name => "::Scaptimony::Asset"
10
+ has_many :asset_policies, :through => :asset, :class_name => "::Scaptimony::AssetPolicy"
11
+ has_many :policies, :through => :asset_policies, :class_name => "::Scaptimony::Policy"
12
+ has_many :arf_reports, :through => :asset, :class_name => '::Scaptimony::ArfReport'
13
+
14
+ scoped_search :in => :policies, :on => :name, :complete_value => true, :rename => :'compliance_policy',
15
+ :only_explicit => true, :operators => ['= ', '!= '], :ext_method => :search_by_policy_name
16
+ scoped_search :in => :policies, :on => :name, :complete_value => true, :rename => :'compliance_report_missing_for',
17
+ :only_explicit => true, :operators => ['= ', '!= '], :ext_method => :search_by_missing_arf
18
+ end
19
+
20
+ def get_asset
21
+ Scaptimony::Asset.where(:assetable_type => 'Host::Base', :assetable_id => id).first_or_create!
10
22
  end
11
23
 
12
- # create or overwrite instance methods...
13
- def instance_method_name
24
+ def policies_enc
25
+ self.policies.map(&:to_enc).to_json
14
26
  end
15
27
 
16
28
  module ClassMethods
17
- # create or overwrite class methods...
18
- def class_method_name
29
+ def search_by_policy_name(key, operator, policy_name)
30
+ cond = sanitize_sql_for_conditions(["scaptimony_policies.name #{operator} ?", value_to_sql(operator, policy_name)])
31
+ { :conditions => Host::Managed.arel_table[:id].in(
32
+ Host::Managed.select(Host::Managed.arel_table[:id]).joins(:policies).where(cond).ast
33
+ ).to_sql }
19
34
  end
20
- end
21
35
 
36
+ def search_by_missing_arf(key, operator, policy_name)
37
+ cond = sanitize_sql_for_conditions(["scaptimony_policies.name #{operator} ?", value_to_sql(operator, policy_name)])
38
+ { :conditions => Host::Managed.arel_table[:id].in(
39
+ Host::Managed.select(Host::Managed.arel_table[:id])
40
+ .joins(:policies)
41
+ .where(cond)
42
+ .where('scaptimony_assets.id not in (
43
+ SELECT distinct scaptimony_arf_reports.asset_id
44
+ FROM scaptimony_arf_reports
45
+ WHERE scaptimony_arf_reports.asset_id = scaptimony_assets.id
46
+ AND scaptimony_arf_reports.policy_id = scaptimony_policies.id)
47
+ ')
48
+ .ast
49
+ ).to_sql
50
+ }
51
+ end
52
+ end
22
53
  end
23
54
  end
@@ -8,13 +8,224 @@
8
8
  # along with this software; if not, see http://www.gnu.org/licenses/gpl.txt
9
9
  #
10
10
 
11
- require 'scaptimony/policy'
12
-
13
11
  module ForemanOpenscap
14
12
  module PolicyExtensions
15
13
  extend ActiveSupport::Concern
14
+
15
+ include Authorizable
16
+ include Taxonomix
17
+
16
18
  included do
17
- scoped_search :on => :name, :complete_value => true
19
+ attr_accessible :location_ids, :organization_ids, :current_step, :hostgroup_ids
20
+ attr_writer :current_step
21
+
22
+ SCAP_PUPPET_CLASS = 'foreman_scap_client'
23
+ POLICIES_CLASS_PARAMETER = 'policies'
24
+ SERVER_CLASS_PARAMETER = 'server'
25
+
26
+ validates :name, :presence => true, :uniqueness => true, :format => { without: /\s/ }
27
+ validate :ensure_needed_puppetclasses
28
+ validates :period, :inclusion => {:in => %w[weekly monthly custom]}, :if => Proc.new { | policy | policy.step_index > 3 }
29
+ validates :weekday, :inclusion => {:in => Date::DAYNAMES.map(&:downcase)},
30
+ :if => Proc.new { | policy | policy.step_index > 3 && policy.period == 'weekly' }
31
+ validates :day_of_month, :numericality => {:greater_than => 0, :less_than => 32},
32
+ :if => Proc.new { | policy | policy.step_index > 3 && policy.period == 'monthly' }
33
+ validate :valid_cron_line
34
+ validate :ensure_period_specification_present
35
+
36
+
37
+ after_save :assign_policy_to_hostgroups
38
+ # before_destroy - ensure that the policy has no hostgroups, or classes
39
+
40
+ default_scope {
41
+ with_taxonomy_scope do
42
+ order("scaptimony_policies.name")
43
+ end
44
+ }
45
+ end
46
+
47
+ def hostgroup_ids
48
+ assets.where(:assetable_type => 'Hostgroup').pluck(:assetable_id)
49
+ end
50
+
51
+ def hostgroup_ids=(ids)
52
+ ids.reject(&:empty?).map do |id|
53
+ self.assets << assets.where(:assetable_type => 'Hostgroup', :assetable_id => id).first_or_create!
54
+ end
55
+ end
56
+
57
+ def hostgroups
58
+ Hostgroup.find(hostgroup_ids)
59
+ end
60
+
61
+ def hostgroups=(hostgroups)
62
+ hostgroup_ids = hostgroups.map(&:id).map(&:to_s)
63
+ end
64
+
65
+ def steps
66
+ base_steps = ['Create policy', 'SCAP Content', 'Schedule']
67
+ base_steps << 'Locations' if SETTINGS[:locations_enabled]
68
+ base_steps << 'Organizations' if SETTINGS[:organizations_enabled]
69
+ base_steps << 'Hostgroups' #always be last.
70
+ end
71
+
72
+ def current_step
73
+ @current_step || steps.first
74
+ end
75
+
76
+ def previous_step
77
+ steps[steps.index(current_step) - 1]
78
+ end
79
+
80
+ def next_step
81
+ steps[steps.index(current_step) + 1 ]
82
+ end
83
+
84
+ def rewind_step
85
+ @current_step = previous_step
86
+ end
87
+
88
+ def first_step?
89
+ current_step == steps.first
90
+ end
91
+
92
+ def last_step?
93
+ current_step == steps.last
94
+ end
95
+
96
+ def wizard_completed?
97
+ current_step.blank?
98
+ end
99
+
100
+ def step_index
101
+ wizard_completed? ? steps.index(steps.last) : steps.index(current_step) + 1
102
+ end
103
+
104
+ def scan_name
105
+ name
106
+ end
107
+
108
+ def used_location_ids
109
+ Location.joins(:taxable_taxonomies).where(
110
+ 'taxable_taxonomies.taxable_type' => 'Scaptimony::Policy',
111
+ 'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
112
+ end
113
+
114
+ def used_organization_ids
115
+ Organization.joins(:taxable_taxonomies).where(
116
+ 'taxable_taxonomies.taxable_type' => 'Scaptimony::Policy',
117
+ 'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
118
+ end
119
+
120
+ def used_hostgroup_ids
121
+ []
122
+ end
123
+
124
+ def assign_hosts(hosts)
125
+ assign_assets hosts.map &:get_asset
126
+ end
127
+
128
+ def to_enc
129
+ {
130
+ 'id' => self.id,
131
+ 'profile_id' => self.scap_content_profile.try(:profile_id) || '',
132
+ 'content_path' => "/var/lib/openscap/content/#{self.scap_content.digest}.xml",
133
+ }.merge(period_enc)
134
+ end
135
+
136
+ private
137
+
138
+ def period_enc
139
+ # get crontab expression as an array (minute hour day_of_month month day_of_week)
140
+ cron_parts = case period
141
+ when 'weekly'
142
+ [ '0', '1', '*', '*', weekday_number.to_s ]
143
+ when 'monthly'
144
+ [ '0', '1', day_of_month.to_s, '*', '*']
145
+ when 'custom'
146
+ cron_line_split
147
+ else
148
+ raise 'invalid period specification'
149
+ end
150
+
151
+ {
152
+ 'minute' => cron_parts[0],
153
+ 'hour' => cron_parts[1],
154
+ 'monthday' => cron_parts[2],
155
+ 'month' => cron_parts[3],
156
+ 'weekday' => cron_parts[4],
157
+ }
158
+ end
159
+
160
+ def weekday_number
161
+ # 0 is sunday, 1 is monday in cron, while DAYS_INTO_WEEK has 0 as monday, 6 as sunday
162
+ (Date::DAYS_INTO_WEEK.with_indifferent_access[weekday] + 1) % 7
163
+ end
164
+
165
+ def ensure_needed_puppetclasses
166
+ unless puppetclass = Puppetclass.find_by_name(SCAP_PUPPET_CLASS)
167
+ errors[:base] << _("Required Puppet class %{class} is not found, please ensure it imported first.") % {:class => SCAP_PUPPET_CLASS}
168
+ return false
169
+ end
170
+
171
+ unless policies_param = puppetclass.class_params.where(:key => POLICIES_CLASS_PARAMETER).first
172
+ errors[:base] << _("Puppet class %{class} does not have %{parameter} class parameter.") % {:class => SCAP_PUPPET_CLASS, :parameter => POLICIES_CLASS_PARAMETER}
173
+ return false
174
+ end
175
+
176
+ policies_param.override = true
177
+ policies_param.key_type = 'array'
178
+ policies_param.default_value = '<%= @host.policies_enc %>'
179
+
180
+ if policies_param.changed? && !policies_param.save
181
+ errors[:base] << _("%{parameter} class parameter for class %{class} could not be configured.") % {:class => SCAP_PUPPET_CLASS, :parameter => POLICIES_CLASS_PARAMETER}
182
+ return false
183
+ end
184
+ end
185
+
186
+ def cron_line_split
187
+ cron_line.split(' ')
188
+ end
189
+
190
+ def valid_cron_line
191
+ return true if period != 'custom' || step_index != 4
192
+
193
+ unless cron_line_split.size == 5
194
+ errors[:base] << _("Cron line does not consist of 5 parts separated by space")
195
+ return false
196
+ end
197
+ end
198
+
199
+ def ensure_period_specification_present
200
+ return true if period.blank? || step_index != 4
201
+
202
+ error = nil
203
+ error = _("You must fill weekday") if weekday.blank? && period == 'weekday'
204
+ error = _("You must fill day of month") if day_of_month.blank? && period == 'monthly'
205
+ error = _("You must fill cron line") if cron_line.blank? && period == 'custom'
206
+ if error
207
+ errors[:base] << error
208
+ return false
209
+ end
210
+ end
211
+
212
+ def assign_policy_to_hostgroups
213
+ if hostgroups.any?
214
+ puppetclass = Puppetclass.find_by_name(SCAP_PUPPET_CLASS)
215
+ hostgroups.each do |hostgroup|
216
+ hostgroup.puppetclasses << puppetclass unless hostgroup.puppetclasses.include? puppetclass
217
+ populate_overrides(puppetclass, hostgroup)
218
+ end
219
+ end
220
+ end
221
+
222
+ def populate_overrides(puppetclass, hostgroup)
223
+ puppetclass.class_params.where(:override => true, :key => SERVER_CLASS_PARAMETER).each do |override|
224
+ if hostgroup.puppet_proxy && (url = hostgroup.puppet_proxy.url).present?
225
+ lookup_value = LookupValue.where(:match => "hostgroup=#{hostgroup.to_label}", :lookup_key_id => override.id).first_or_initialize
226
+ lookup_value.update_attribute(:value, url)
227
+ end
228
+ end
18
229
  end
19
230
  end
20
231
  end
@@ -13,8 +13,28 @@ require 'scaptimony/scap_content'
13
13
  module ForemanOpenscap
14
14
  module ScapContentExtensions
15
15
  extend ActiveSupport::Concern
16
+ include Authorizable
17
+ include Taxonomix
16
18
  included do
17
- scoped_search :on => :digest
19
+ attr_accessible :location_ids, :organization_ids
20
+
21
+ default_scope {
22
+ with_taxonomy_scope do
23
+ order("scaptimony_scap_contents.title")
24
+ end
25
+ }
26
+ end
27
+
28
+ def used_location_ids
29
+ Location.joins(:taxable_taxonomies).where(
30
+ 'taxable_taxonomies.taxable_type' => 'Scaptimony::ScapContent',
31
+ 'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
32
+ end
33
+
34
+ def used_organization_ids
35
+ Organization.joins(:taxable_taxonomies).where(
36
+ 'taxable_taxonomies.taxable_type' => 'Scaptimony::ScapContent',
37
+ 'taxable_taxonomies.taxable_id' => id).pluck("#{Location.arel_table.name}.id")
18
38
  end
19
39
  end
20
40
  end