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.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_openscap/load_report.js +1 -1
  3. data/app/assets/javascripts/foreman_openscap/policy_edit.js +0 -2
  4. data/app/controllers/api/v2/compliance/arf_reports_controller.rb +7 -22
  5. data/app/controllers/api/v2/compliance/policies_controller.rb +5 -5
  6. data/app/controllers/api/v2/compliance/scap_contents_controller.rb +3 -3
  7. data/app/controllers/arf_reports_controller.rb +62 -0
  8. data/app/controllers/{scaptimony_dashboard_controller.rb → compliance_dashboard_controller.rb} +1 -1
  9. data/app/controllers/compliance_hosts_controller.rb +5 -0
  10. data/app/controllers/{scaptimony_policies_controller.rb → policies_controller.rb} +13 -13
  11. data/app/controllers/{scaptimony_policy_dashboard_controller.rb → policy_dashboard_controller.rb} +3 -3
  12. data/app/controllers/{scaptimony_scap_contents_controller.rb → scap_contents_controller.rb} +11 -11
  13. data/app/helpers/{scaptimony_report_dashboard_helper.rb → arf_report_dashboard_helper.rb} +3 -5
  14. data/app/helpers/arf_reports_helper.rb +21 -0
  15. data/app/helpers/compliance_hosts_helper.rb +25 -0
  16. data/app/helpers/concerns/foreman_openscap/hosts_helper_extensions.rb +2 -32
  17. data/app/helpers/{scaptimony_policies_helper.rb → policies_helper.rb} +6 -2
  18. data/app/helpers/{scaptimony_policy_dashboard_helper.rb → policy_dashboard_helper.rb} +8 -8
  19. data/app/lib/proxy_api/available_proxy.rb +26 -0
  20. data/app/lib/proxy_api/openscap.rb +40 -0
  21. data/app/mailers/foreman_openscap/policy_mailer.rb +42 -0
  22. data/app/models/concerns/foreman_openscap/compliance_status_scoped_search.rb +91 -0
  23. data/app/models/concerns/foreman_openscap/host_extensions.rb +73 -17
  24. data/app/models/concerns/foreman_openscap/hostgroup_extensions.rb +3 -5
  25. data/app/models/foreman_openscap/arf_report.rb +165 -0
  26. data/app/models/foreman_openscap/asset.rb +27 -0
  27. data/app/models/foreman_openscap/asset_policy.rb +6 -0
  28. data/app/models/foreman_openscap/compliance_status.rb +50 -0
  29. data/app/models/{concerns/foreman_openscap/policy_extensions.rb → foreman_openscap/policy.rb} +72 -45
  30. data/app/models/foreman_openscap/policy_arf_report.rb +8 -0
  31. data/app/models/foreman_openscap/policy_revision.rb +6 -0
  32. data/app/models/foreman_openscap/scap_content.rb +112 -0
  33. data/app/models/foreman_openscap/scap_content_profile.rb +6 -0
  34. data/app/overrides/hosts/overview/host_compliance_status.rb +4 -4
  35. data/app/services/foreman_openscap/arf_report_status_calculator.rb +45 -0
  36. data/app/services/{scaptimony → foreman_openscap}/host_report_dashboard/data.rb +12 -6
  37. data/app/services/{scaptimony → foreman_openscap}/policy_dashboard/data.rb +5 -5
  38. data/app/services/{scaptimony → foreman_openscap}/report_dashboard/data.rb +4 -4
  39. data/app/views/api/v2/compliance/policies/create.json.rabl +3 -0
  40. data/app/views/{scaptimony_arf_reports → arf_reports}/_list.html.erb +4 -4
  41. data/app/views/arf_reports/_metrics.html.erb +37 -0
  42. data/app/views/arf_reports/_output.html.erb +23 -0
  43. data/app/views/{scaptimony_arf_reports → arf_reports}/index.html.erb +0 -0
  44. data/app/views/arf_reports/show.html.erb +14 -0
  45. data/app/views/{scaptimony_arf_reports/show.html.erb → arf_reports/show_html.html.erb} +2 -3
  46. data/app/views/compliance_hosts/_compliance_status.erb +6 -0
  47. data/app/views/{scaptimony_hosts → compliance_hosts}/show.html.erb +9 -2
  48. data/app/views/dashboard/{_foreman_openscap_host_reports_widget.html.erb → _compliance_host_reports_widget.html.erb} +3 -3
  49. data/app/views/dashboard/{_foreman_openscap_reports_breakdown_widget.html.erb → _compliance_reports_breakdown_widget.html.erb} +1 -1
  50. data/app/views/foreman_openscap/policy_mailer/_dashboard.erb +21 -0
  51. data/app/views/foreman_openscap/policy_mailer/_hosts.erb +44 -0
  52. data/app/views/foreman_openscap/policy_mailer/_list.erb +10 -0
  53. data/app/views/foreman_openscap/policy_mailer/_policy.erb +7 -0
  54. data/app/views/foreman_openscap/policy_mailer/policy_summary.erb +19 -0
  55. data/app/views/{scaptimony_policies → policies}/_form.html.erb +2 -8
  56. data/app/views/{scaptimony_policies → policies}/_list.html.erb +5 -5
  57. data/app/views/policies/_scap_content_results.html.erb +3 -0
  58. data/app/views/policies/create.html.erb +2 -0
  59. data/app/views/{scaptimony_policies → policies}/disassociate_multiple_hosts.html.erb +2 -2
  60. data/app/views/{scaptimony_policies → policies}/edit.html.erb +0 -0
  61. data/app/views/{scaptimony_policies → policies}/index.html.erb +1 -1
  62. data/app/views/policies/new.html.erb +2 -0
  63. data/app/views/{scaptimony_policies → policies}/select_multiple_hosts.html.erb +2 -2
  64. data/app/views/{scaptimony_policies → policies}/show.html.erb +1 -1
  65. data/app/views/{scaptimony_policies → policies}/steps/_create_policy_form.html.erb +0 -0
  66. data/app/views/{scaptimony_policies → policies}/steps/_hostgroups_form.html.erb +0 -0
  67. data/app/views/{scaptimony_policies → policies}/steps/_locations_form.html.erb +0 -0
  68. data/app/views/{scaptimony_policies → policies}/steps/_organizations_form.html.erb +0 -0
  69. data/app/views/policies/steps/_scap_content_form.html.erb +9 -0
  70. data/app/views/{scaptimony_policies → policies}/steps/_schedule_form.html.erb +1 -1
  71. data/app/views/{scaptimony_policies → policies}/steps/_step_form.html.erb +3 -3
  72. data/app/views/{scaptimony_policies → policies}/welcome.html.erb +2 -2
  73. data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_chart_widget.html.erb +0 -0
  74. data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_reports.html.erb +2 -2
  75. data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/_policy_status_widget.html.erb +3 -3
  76. data/app/views/{scaptimony_policy_dashboard → policy_dashboard}/index.html.erb +0 -0
  77. data/app/views/{scaptimony_scap_contents → scap_contents}/_form.html.erb +5 -6
  78. data/app/views/{scaptimony_scap_contents → scap_contents}/_list.html.erb +3 -3
  79. data/app/views/{scaptimony_scap_contents → scap_contents}/edit.html.erb +0 -0
  80. data/app/views/{scaptimony_scap_contents → scap_contents}/index.html.erb +1 -1
  81. data/app/views/{scaptimony_scap_contents → scap_contents}/new.html.erb +0 -0
  82. data/app/views/{scaptimony_scap_contents → scap_contents}/welcome.html.erb +2 -2
  83. data/config/routes.rb +15 -11
  84. data/db/migrate/20141013172051_create_scaptimony_policies.rb +9 -0
  85. data/db/migrate/20141014105333_create_scaptimony_assets.rb +10 -0
  86. data/db/migrate/20141015092642_create_scaptimony_arf_reports.rb +13 -0
  87. data/db/migrate/20141015115511_add_arf_report_unique_constraint.rb +6 -0
  88. data/db/migrate/20141104164201_create_scaptimony_scap_contents.rb +7 -0
  89. data/db/migrate/20141104171545_create_scaptimony_policy_revisions.rb +14 -0
  90. data/db/migrate/20141105174625_add_description_to_scaptimony_policy_revisions.rb +5 -0
  91. data/db/migrate/20141105174834_add_columns_to_scaptimony_policies.rb +12 -0
  92. data/db/migrate/20141107091756_add_columns_to_scaptimony_scap_contents.rb +8 -0
  93. data/db/migrate/20141111104519_add_constraint_to_scaptimony_scap_contents.rb +5 -0
  94. data/db/migrate/20141113221054_create_scaptimony_scap_content_profiles.rb +12 -0
  95. data/db/migrate/20141116170632_remove_xccdf_profile_from_scaptimony_policies.rb +5 -0
  96. data/db/migrate/20141116171305_add_profile_to_scaptimony_policies.rb +6 -0
  97. data/db/migrate/20141118142954_add_constraint_to_scaptimony_policies.rb +5 -0
  98. data/db/migrate/20141119164918_create_scaptimony_xccdf_results.rb +8 -0
  99. data/db/migrate/20141119175434_create_scaptimony_xccdf_rules.rb +8 -0
  100. data/db/migrate/20141119182606_create_scaptimony_xccdf_rule_results.rb +9 -0
  101. data/db/migrate/20141121120326_create_scaptimony_arf_report_breakdowns.rb +24 -0
  102. data/db/migrate/20141121164042_replace_arf_report_breakdown_view.rb +25 -0
  103. data/db/migrate/20141206211151_create_scaptimony_assets_policies.rb +9 -0
  104. data/db/migrate/20141214112917_add_scap_file_to_scap_content.rb +5 -0
  105. data/db/migrate/20141216154502_rename_scaptimony_asset_policies.rb +5 -0
  106. data/db/migrate/20150111085317_polymorph_asset.rb +8 -0
  107. data/db/migrate/20150112152944_create_scaptimony_arf_report_raws.rb +10 -0
  108. data/db/migrate/20150114210634_rename_scaptimony_arf_report_raw_raw.rb +5 -0
  109. data/db/migrate/20150115155947_add_scaptimony_scap_content_digest.rb +21 -0
  110. data/db/migrate/20150116083129_add_day_of_month_and_cron_line_to_scaptimony_policy.rb +6 -0
  111. data/db/migrate/20150821100137_migrate_from_scaptimony.rb +59 -0
  112. data/db/migrate/20150827123826_remove_scaptimony_permissions.rb +21 -0
  113. data/db/migrate/20150925124959_create_policy_arf_reports.rb +13 -0
  114. data/db/migrate/20150929124853_add_result_to_logs.rb +9 -0
  115. data/db/migrate/20150929152345_move_arf_reports_to_reports_table.rb +179 -0
  116. data/db/migrate/20151023131950_link_arf_report_directly_to_host.rb +17 -0
  117. data/db/seeds.d/openscap_policy_notification.rb +9 -0
  118. data/lib/foreman_openscap/bulk_upload.rb +3 -1
  119. data/lib/foreman_openscap/engine.rb +53 -42
  120. data/lib/foreman_openscap/helper.rb +8 -0
  121. data/lib/foreman_openscap/version.rb +1 -1
  122. data/lib/tasks/foreman_openscap_tasks.rake +14 -0
  123. data/test/factories/arf_report_factory.rb +9 -6
  124. data/test/factories/asset_factory.rb +1 -1
  125. data/test/factories/compliance_host_factory.rb +9 -0
  126. data/test/factories/compliance_log_factory.rb +11 -0
  127. data/test/factories/policy_arf_report_factory.rb +6 -0
  128. data/test/factories/policy_factory.rb +3 -2
  129. data/test/factories/scap_content_related.rb +2 -2
  130. data/test/functional/api/v2/compliance/arf_reports_controller_test.rb +4 -3
  131. data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -2
  132. data/test/functional/api/v2/compliance/scap_contents_controller_test.rb +3 -1
  133. data/test/lib/foreman_openscap/bulk_upload_test.rb +1 -1
  134. data/test/test_plugin_helper.rb +30 -0
  135. data/test/unit/arf_report_status_calculator_test.rb +11 -0
  136. data/test/unit/arf_report_test.rb +148 -0
  137. data/test/unit/compliance_status_test.rb +71 -0
  138. data/test/unit/openscap_host_test.rb +38 -7
  139. data/test/unit/policy_mailer_test.rb +38 -0
  140. data/test/unit/scap_content_test.rb +32 -0
  141. metadata +130 -74
  142. data/app/controllers/scaptimony_arf_reports_controller.rb +0 -34
  143. data/app/controllers/scaptimony_hosts_controller.rb +0 -5
  144. data/app/models/concerns/foreman_openscap/arf_report_extensions.rb +0 -50
  145. data/app/models/concerns/foreman_openscap/asset_extensions.rb +0 -34
  146. data/app/models/concerns/foreman_openscap/scap_content_extensions.rb +0 -40
  147. data/app/overrides/hosts/index/host_arf_report.rb +0 -5
  148. data/app/views/scaptimony_arf_reports/_host_report.html.erb +0 -8
  149. data/app/views/scaptimony_hosts/_host_status.html.erb +0 -17
  150. data/app/views/scaptimony_policies/_scap_content_results.html.erb +0 -7
  151. data/app/views/scaptimony_policies/create.html.erb +0 -2
  152. data/app/views/scaptimony_policies/new.html.erb +0 -2
  153. 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 => "::Scaptimony::Asset"
9
- has_many :asset_policies, :through => :asset, :class_name => "::Scaptimony::AssetPolicy"
10
- has_many :policies, :through => :asset_policies, :class_name => "::Scaptimony::Policy"
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,6 @@
1
+ module ForemanOpenscap
2
+ class AssetPolicy < ActiveRecord::Base
3
+ belongs_to :policy
4
+ belongs_to :asset
5
+ end
6
+ 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
@@ -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
- module PolicyExtensions
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
- included do
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
- PORT_CLASS_PARAMETER = 'port'
26
-
27
- validates :name, :presence => true, :uniqueness => true, :format => {without: /\s/}
28
- validate :ensure_needed_puppetclasses
29
- validates :period, :inclusion => {:in => %w[weekly monthly custom]},
30
- :if => Proc.new { |policy| policy.new_record? ? policy.step_index > 3 : !policy.id.blank? }
31
- validates :weekday, :inclusion => {:in => Date::DAYNAMES.map(&:downcase)},
32
- :if => Proc.new { |policy| policy.period == 'weekly' && (policy.new_record? ? policy.step_index > 3 : !policy.id.blank?) }
33
- validates :day_of_month, :numericality => {:greater_than => 0, :less_than => 32},
34
- :if => Proc.new { |policy| policy.period == 'monthly'&& (policy.new_record? ? policy.step_index > 3 : !policy.id.blank?) }
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' => 'Scaptimony::Policy',
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' => 'Scaptimony::Policy',
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 = Scaptimony::Asset.where(:assetable_type => 'Host::Base', :assetable_id => hosts.map(&:id)).pluck(:id)
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,8 @@
1
+ module ForemanOpenscap
2
+ class PolicyArfReport < ::ActiveRecord::Base
3
+ belongs_to :arf_report
4
+ belongs_to :policy
5
+
6
+ scope :of_policy, lambda { |policy_id| joins(:policy).where(:policy_id => policy_id) }
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module ForemanOpenscap
2
+ class PolicyRevision < ActiveRecord::Base
3
+ belongs_to :policy
4
+ belongs_to :scap_content
5
+ end
6
+ end
@@ -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