foreman_openscap 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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