Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #31685 - Add treshold for ds policies #519

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Foreman::Controller::Parameters::PolicyApi

class_methods do
def filter_params_list
[:description, :name, :period, :scap_content_id, :scap_content_profile_id, :deploy_by,
[:description, :name, :treshold, :period, :scap_content_id, :scap_content_profile_id, :deploy_by,
:weekday, :day_of_month, :cron_line, :tailoring_file_id, :tailoring_file_profile_id,
:location_ids => [], :organization_ids => [], :hostgroup_ids => [], :host_ids => []]
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ def search_by_not_comply_with(_key, _operator, policy_name)
search_by_policy_results policy_name, &:failed
end

def search_by_inconclusive_with(_key, _operator, policy_name)
search_by_policy_results policy_name, &:othered
end

def search_by_policy_results(policy_name, &selection)
scope = ArfReport.of_policy(Policy.find_by(:name => policy_name).id)
.instance_eval(&selection)
Expand Down Expand Up @@ -67,8 +63,6 @@ def search_by_compliance_status(key, operator, value)
ArfReport.passed
when 'incompliant'
ArfReport.failed
when 'inconclusive'
ArfReport.othered
end
query_conditions_from_scope scope
end
Expand Down Expand Up @@ -131,9 +125,6 @@ def apply_condition(scope, negate, conditions)
scoped_search :relation => :policy, :on => :name, :complete_value => true, :rename => :not_comply_with,
:only_explicit => true, :operators => ['= '], :ext_method => :search_by_not_comply_with

scoped_search :relation => :policy, :on => :name, :complete_value => true, :rename => :inconclusive_with,
:only_explicit => true, :operators => ['= '], :ext_method => :search_by_inconclusive_with

scoped_search :relation => :openscap_proxy, :on => :name, :complete_value => true, :only_explicit => true, :rename => :openscap_proxy

scoped_search :relation => :sources, :on => :value, :rename => :xccdf_rule_name,
Expand All @@ -151,9 +142,8 @@ def apply_condition(scope, negate, conditions)
scoped_search :on => :status, :rename => :compliance_status, :operators => ['= '],
:ext_method => :search_by_compliance_status,
:complete_value => { :compliant => ::ForemanOpenscap::ComplianceStatus::COMPLIANT,
:incompliant => ::ForemanOpenscap::ComplianceStatus::INCOMPLIANT,
:inconclusive => ::ForemanOpenscap::ComplianceStatus::INCONCLUSIVE },
:validator => ->(value) { ['compliant', 'incompliant', 'inconclusive'].reduce(false) { |memo, item| memo || (item == value) } }
:incompliant => ::ForemanOpenscap::ComplianceStatus::INCOMPLIANT },
:validator => ->(value) { ['compliant', 'incompliant'].reduce(false) { |memo, item| memo || (item == value) } }
end
end
end
25 changes: 7 additions & 18 deletions app/models/foreman_openscap/arf_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,9 @@ class ArfReport < ::Report
ON reports.id = latest.id")
}

scope :failed, lambda { where("(#{report_status_column} >> #{bit_mask 'failed'}) > 0") }
scope :not_failed, lambda { where("(#{report_status_column} >> #{bit_mask 'failed'}) = 0") }

scope :othered, lambda { where("(#{report_status_column} >> #{bit_mask 'othered'}) > 0").merge(not_failed) }
scope :not_othered, lambda { where("(#{report_status_column} >> #{bit_mask 'othered'}) = 0") }

scope :passed, lambda { where("(#{report_status_column} >> #{bit_mask 'passed'}) > 0").merge(not_failed).merge(not_othered) }
scope :with_score, lambda { where.not(:score => nil) }
scope :passed, lambda { with_score.includes(:policy).where('foreman_openscap_policies.treshold <= reports.score') }
scope :failed, lambda { where.not(:id => ArfReport.passed.pluck(:id)) }

scope :by_rule_result, lambda { |rule_name, rule_result| joins(:sources).where(:sources => { :value => rule_name }, :logs => { :result => rule_result }) }

Expand Down Expand Up @@ -93,11 +89,7 @@ def passed
end

def failed
status_of "failed"
end

def othered
status_of "othered"
status_of("failed") + status_of("othered")
end

def rules_count
Expand All @@ -114,6 +106,7 @@ def self.create_arf(asset, proxy, params)
:reported_at => Time.at(params[:date].to_i),
:status => params[:metrics],
:metrics => params[:metrics],
:score => params[:score],
:openscap_proxy => proxy)
return arf_report unless arf_report.persisted?
PolicyArfReport.where(:arf_report_id => arf_report.id, :policy_id => policy.id, :digest => params[:digest]).first_or_create!
Expand Down Expand Up @@ -162,15 +155,11 @@ def assign_locations_organizations
end

def failed?
failed > 0
!passed?
end

def passed?
passed > 0 && failed == 0 && othered == 0
end

def othered?
!passed? && !failed?
score && score >= policy.treshold
end

def to_html
Expand Down
1 change: 0 additions & 1 deletion app/models/foreman_openscap/compliance_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ def relevant?(options = {})
def to_status(options = {})
latest_reports = host.policies.map { |p| host.last_report_for_policy p }.flatten
return INCOMPLIANT if latest_reports.any?(&:failed?)
return INCONCLUSIVE if latest_reports.any?(&:othered?)
COMPLIANT
end
end
Expand Down
27 changes: 26 additions & 1 deletion app/models/foreman_openscap/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def self.deploy_by_variants

validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 255 },
:if => Proc.new { |policy| policy.should_validate?('Policy Attributes') }
validates :treshold, :presence => true, :if => Proc.new { |policy| policy.should_validate?('Policy Attributes') }

validates :period, :inclusion => { :in => %w[weekly monthly custom], :message => _('is not a valid value') },
:if => Proc.new { |policy| policy.should_validate?('Schedule') }
validates :deploy_by, :inclusion => { :in => Policy.deploy_by_variants },
Expand All @@ -39,9 +41,10 @@ def self.deploy_by_variants
validates :scap_content_id, presence: true, if: Proc.new { |policy| policy.should_validate?('SCAP Content') }
validate :matching_content_profile, if: Proc.new { |policy| policy.should_validate?('SCAP Content') }

validate :valid_tailoring, :valid_tailoring_profile, :no_mixed_deployments
validate :valid_tailoring, :valid_tailoring_profile, :no_mixed_deployments, :treshold_value
validate :valid_cron_line, :valid_weekday, :valid_day_of_month, :if => Proc.new { |policy| policy.should_validate?('Schedule') }
after_save :assign_policy_to_hostgroups
after_save :refresh_compliance_status
# before_destroy - ensure that the policy has no hostgroups, or classes

default_scope do
Expand Down Expand Up @@ -107,6 +110,17 @@ def hosts=(hosts)
host_ids = hosts.map(&:id).map(&:to_s)
end

def all_hosts
hg_ids = hostgroups.flat_map(&:subtree_ids).uniq
(Host.where(:hostgroup_id => hg_ids) + hosts).uniq
end

def refresh_host_statuses
all_hosts.map do |host|
host.refresh_statuses([HostStatus.find_status_by_humanized_name("compliance")])
end
end

def step_to_i(step_name)
steps.index(step_name) + 1
end
Expand Down Expand Up @@ -272,5 +286,16 @@ def no_mixed_deployments
end
end
end

def treshold_value
if (100 < treshold || treshold < 0) && should_validate?('Policy Attributes')
errors.add(:treshold, _("must be between 0 and 100"))
end
end

def refresh_compliance_status
return if id_previously_changed?
refresh_host_statuses
end
end
end
1 change: 1 addition & 0 deletions app/views/policies/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<div class="tab-pane" id="primary">
<%= text_f(f, :name) %>
<%= textarea_f(f, :description, :rows => :auto ) %>
<%= counter_f(f, :treshold) %>
</div>
<div class="tab-pane" id="scap_content">
<%= scap_content_selector(f) %>
Expand Down
1 change: 1 addition & 0 deletions app/views/policies/steps/_policy_attributes_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
<%= wizard_header @policy.step_index, *translate_steps(@policy) %>
<%= text_f(f, :name) %>
<%= text_f(f, :description, :size => "col-md-8" ) %>
<%= counter_f(f, :treshold) %>
</div>
6 changes: 6 additions & 0 deletions db/migrate/20210126144230_add_treshold_to_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddTresholdToPolicy < ActiveRecord::Migration[6.0]
def change
add_column :foreman_openscap_policies, :treshold, :numeric, :null => false, :default => 100
add_column :reports, :score, :numeric
end
end