foreman_openscap 0.11.5 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/compliance/arf_reports_controller.rb +3 -6
  3. data/app/controllers/api/v2/compliance/policies_controller.rb +3 -1
  4. data/app/controllers/concerns/foreman/controller/parameters/policy_api.rb +1 -1
  5. data/app/controllers/policies_controller.rb +2 -1
  6. data/app/helpers/foreman_openscap_helper.rb +14 -0
  7. data/app/helpers/policies_helper.rb +36 -0
  8. data/app/models/concerns/foreman_openscap/host_extensions.rb +5 -1
  9. data/app/models/concerns/foreman_openscap/openscap_proxy_core_extensions.rb +15 -19
  10. data/app/models/foreman_openscap/policy.rb +14 -80
  11. data/app/services/foreman_openscap/client_config/ansible.rb +38 -0
  12. data/app/services/foreman_openscap/client_config/base.rb +41 -0
  13. data/app/services/foreman_openscap/client_config/manual.rb +27 -0
  14. data/app/services/foreman_openscap/client_config/puppet.rb +38 -0
  15. data/app/services/foreman_openscap/config_name_service.rb +29 -0
  16. data/app/services/foreman_openscap/hostgroup_overrider.rb +71 -0
  17. data/app/services/foreman_openscap/lookup_key_overrider.rb +94 -0
  18. data/app/views/policies/_form.html.erb +6 -2
  19. data/app/views/policies/steps/_deployment_options_form.html.erb +11 -0
  20. data/app/views/policies/steps/{_create_policy_form.html.erb → _policy_attributes_form.html.erb} +0 -0
  21. data/db/migrate/20190103093409_add_deployment_option_to_policy.foreman_openscap.rb +15 -0
  22. data/lib/foreman_openscap/engine.rb +3 -3
  23. data/lib/foreman_openscap/version.rb +1 -1
  24. data/locale/de/foreman_openscap.edit.po +302 -106
  25. data/locale/en_GB/foreman_openscap.edit.po +302 -106
  26. data/locale/es/foreman_openscap.edit.po +302 -106
  27. data/locale/fr/foreman_openscap.edit.po +302 -106
  28. data/locale/gl/foreman_openscap.edit.po +302 -106
  29. data/locale/it/foreman_openscap.edit.po +302 -106
  30. data/locale/ja/foreman_openscap.edit.po +302 -106
  31. data/locale/ko/foreman_openscap.edit.po +302 -106
  32. data/locale/pt_BR/foreman_openscap.edit.po +302 -106
  33. data/locale/ru/foreman_openscap.edit.po +302 -106
  34. data/locale/sv_SE/foreman_openscap.edit.po +302 -106
  35. data/locale/zh_CN/foreman_openscap.edit.po +302 -106
  36. data/locale/zh_TW/foreman_openscap.edit.po +302 -106
  37. data/test/factories/policy_factory.rb +1 -0
  38. data/test/functional/api/v2/compliance/arf_reports_controller_test.rb +2 -3
  39. data/test/functional/api/v2/compliance/policies_controller_test.rb +2 -2
  40. data/test/test_plugin_helper.rb +26 -7
  41. data/test/unit/openscap_host_test.rb +0 -2
  42. data/test/unit/policy_test.rb +32 -23
  43. data/test/unit/services/config_name_service_test.rb +39 -0
  44. data/test/unit/services/hostgroup_overrider_test.rb +78 -0
  45. data/test/unit/services/lookup_key_overrider_test.rb +52 -0
  46. metadata +19 -5
  47. data/test/unit/puppet_overrides_test.rb +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 476be55fdeeda480329624fd4ca562c93e33c370
4
- data.tar.gz: 7d58930838e01c45d99fcd9b11444696c336eaa0
3
+ metadata.gz: 32dd2a50a02e9810d04dbb252858b165e734c999
4
+ data.tar.gz: 40bd3d5ca9a1f27d8904fb07298772d624ac8728
5
5
  SHA512:
6
- metadata.gz: d2bac463d3ffd03167b30a84d38194d6a5a12e8d331c09a0cbe4dabbd2ad2c785652b4bfe2ed873bd1af80ed24aeb73bd36459e0db2ecc067af975a6777d013f
7
- data.tar.gz: 4cc7c132c045f5baf2a30e92cbc9bc55f39e4ae4eea23c044f87c981057e38a82b55c4a52e6ad08d3dc406e79c425115636110877d945d7e6fb4c20897b7d5be
6
+ metadata.gz: 51df4d59920bcc1e1467837d08ec086c4a9f3d304d39836f38b57654b5b5dbdb1f298feb426454e39623eefe5d984f6f4e5dc9c9ee8efb3a462fa3b58d71715b
7
+ data.tar.gz: 9f8b8ae9ac001d65a8bdcd773c7d729129ae889b080412bb7cc346913919739c9f079cd0ac78d5f1619b97beacc4efcea341dee48e1990238d1f39e9dac162ea
@@ -76,7 +76,9 @@ module Api
76
76
  private
77
77
 
78
78
  def respond_for_report(arf_report)
79
- if arf_report.new_record?
79
+ if arf_report.nil?
80
+ upload_fail(_("Policy with id %s not found.") % params[:policy_id])
81
+ elsif arf_report.new_record?
80
82
  upload_fail arf_report.errors.full_messages.to_sentence
81
83
  else
82
84
  render :json => { :result => :ok, :id => arf_report.id.to_s }
@@ -89,11 +91,6 @@ module Api
89
91
  end
90
92
 
91
93
  def find_resources_before_create
92
- unless ForemanOpenscap::Policy.where(:id => params[:policy_id]).any?
93
- upload_fail(_("Policy with id %s not found.") % params[:policy_id])
94
- return
95
- end
96
-
97
94
  @asset = ForemanOpenscap::Helper::get_asset(params[:cname], params[:policy_id])
98
95
 
99
96
  unless @asset
@@ -49,6 +49,7 @@ module Api::V2
49
49
  param :host_ids, Array, :desc => N_('Apply policy to hosts')
50
50
  param :tailoring_file_id, Integer, :desc => N_('Tailoring file ID')
51
51
  param :tailoring_file_profile_id, Integer, :desc => N_('Tailoring file profile ID')
52
+ param :deploy_by, ForemanOpenscap::Policy.deploy_by_variants, :desc => N_('How the policy should be deployed')
52
53
  param_group :taxonomies, ::Api::V2::BaseController
53
54
  end
54
55
  end
@@ -58,7 +59,8 @@ module Api::V2
58
59
 
59
60
  def create
60
61
  @policy = ForemanOpenscap::Policy.new(policy_params)
61
- process_response @policy.save
62
+ ForemanOpenscap::LookupKeyOverrider.new(@policy).override
63
+ process_response(@policy.errors.none? && @policy.save)
62
64
  end
63
65
 
64
66
  api :PUT, '/compliance/policies/:id', N_('Update a Policy')
@@ -3,7 +3,7 @@ module Foreman::Controller::Parameters::PolicyApi
3
3
 
4
4
  class_methods do
5
5
  def filter_params_list
6
- [:description, :name, :period, :scap_content_id, :scap_content_profile_id,
6
+ [:description, :name, :period, :scap_content_id, :scap_content_profile_id, :deploy_by,
7
7
  :weekday, :day_of_month, :cron_line, :tailoring_file_id, :tailoring_file_profile_id,
8
8
  :location_ids => [], :organization_ids => [], :hostgroup_ids => [], :host_ids => []]
9
9
  end
@@ -32,9 +32,10 @@ class PoliciesController < ApplicationController
32
32
 
33
33
  def create
34
34
  @policy = ::ForemanOpenscap::Policy.new(policy_params)
35
+ ForemanOpenscap::LookupKeyOverrider.new(@policy).override if @policy.current_step?('Policy Attributes')
35
36
  if @policy.wizard_completed? && @policy.save
36
37
  process_success :success_redirect => policies_path
37
- elsif @policy.valid?
38
+ elsif @policy.errors.none? && @policy.valid?
38
39
  render('new') && return
39
40
  else
40
41
  @policy.rewind_step
@@ -0,0 +1,14 @@
1
+ module ForemanOpenscapHelper
2
+ def scap_doc_link(section = '', text = _('documentation'))
3
+ link_to(
4
+ text,
5
+ scap_doc_url(section),
6
+ :rel => 'external noopener noreferrer', :target => '_blank'
7
+ )
8
+ end
9
+
10
+ def scap_doc_url(section = '')
11
+ version = ForemanOpenscap::VERSION.split('.')[0..-2].join('.')
12
+ "https://theforeman.org/plugins/foreman_openscap/#{version}/index.html#{section}"
13
+ end
14
+ end
@@ -9,6 +9,42 @@ module PoliciesHelper
9
9
  policy.scap_content_profile.nil? ? "Default" : policy.scap_content_profile.title
10
10
  end
11
11
 
12
+ def deploy_by_radios(f, policy)
13
+ ForemanOpenscap::ConfigNameService.new.configs.map do |tool|
14
+ popover_block = popover("", config_inline_help(tool.inline_help))
15
+
16
+ label = label_tag('', :class => 'col-md-2 control-label') do
17
+ tool.type.to_s.capitalize.html_safe + ' ' + popover_block.html_safe
18
+ end
19
+
20
+ radio = content_tag(:div, :class => "col-md-2") do
21
+ f.radio_button(:deploy_by, tool.type, :disabled => !tool.available?, :checked => deploy_by_radio_checked(policy, tool))
22
+ end
23
+
24
+ content_tag(:div, :class => "clearfix") do
25
+ content_tag(:div, :class => "form-group") do
26
+ label.html_safe + radio.html_safe
27
+ end
28
+ end
29
+ end.join('').html_safe
30
+ end
31
+
32
+ def config_inline_help(help_hash)
33
+ link = if help_hash[:route_helper_method]
34
+ link_to_if_authorized help_hash[:replace_text], public_send(help_hash[:route_helper_method])
35
+ else
36
+ help_hash[:replace_text]
37
+ end
38
+ text = help_hash[:text]
39
+ text = text.split(help_hash[:replace_text], 2).join(link) if help_hash.key?(:replace_text)
40
+ text.html_safe
41
+ end
42
+
43
+ def deploy_by_radio_checked(policy, tool)
44
+ type = policy.deploy_by ? policy.deploy_by.to_sym : :puppet
45
+ tool.type == type
46
+ end
47
+
12
48
  def effective_policy_profile(policy)
13
49
  policy.tailoring_file ? policy.tailoring_file_profile.title : policy_profile_from_scap_content(policy)
14
50
  end
@@ -36,7 +36,11 @@ module ForemanOpenscap
36
36
  base.scoped_search :on => :id, :rename => :others_xccdf_rule,
37
37
  :only_explicit => true, :operators => ['= '], :ext_method => :search_by_rule_othered
38
38
 
39
- base.after_update :puppetrun!, :if => ->(host) { Setting[:puppetrun] && host.changed.include?('openscap_proxy_id') }
39
+ base.after_update :puppetrun!, :if => ->(host) do
40
+ Setting[:puppetrun] &&
41
+ host.changed.include?('openscap_proxy_id') &&
42
+ (host.individual_puppetclasses + host.parent_classes).pluck(:name).include?(ClientConfig::Puppet.new.puppetclass_name)
43
+ end
40
44
 
41
45
  base.scope :comply_with, lambda { |policy|
42
46
  joins(:arf_reports).merge(ArfReport.latest_of_policy(policy)).merge(ArfReport.passed)
@@ -4,22 +4,28 @@ module ForemanOpenscap
4
4
 
5
5
  included do
6
6
  validate :openscap_proxy_has_feature
7
- validate :scap_client_class_present
8
7
  after_save :update_scap_client
9
8
  end
10
9
 
11
10
  def update_scap_client
12
- update_scap_client_params if openscap_proxy_id_previously_changed?
11
+ name_service = ConfigNameService.new
12
+ if openscap_proxy_id_previously_changed?
13
+ model_match = self.class.name.underscore =~ /\Ahostgroup\z/ ? "hostgroup" : "fqdn"
14
+ name_service.all_available_except(:manual).each do |config|
15
+ update_client_params(model_match, config)
16
+ end
17
+ end
13
18
  end
14
19
 
15
- def update_scap_client_params
16
- model_match = self.class.name.underscore =~ /\Ahostgroup\z/ ? "hostgroup" : "fqdn"
17
- scap_params = find_scap_client.class_params
18
- server_lookup_key = scap_params.find { |param| param.key == "server" }
19
- port_lookup_key = scap_params.find { |param| param.key == "port" }
20
- pairs = scap_client_lookup_values_for([server_lookup_key, port_lookup_key], model_match)
20
+ def update_client_params(model_match, config)
21
+ client_item = config.find_config_item self.public_send(config.collection_method)
22
+ return unless client_item
23
+ lookup_keys = client_item.public_send(config.override_method_name)
24
+ server_key = lookup_keys.find { |param| param.key == config.server_param }
25
+ port_key = lookup_keys.find { |param| param.key == config.port_param }
26
+ pairs = scap_client_lookup_values_for([server_key, port_key], model_match)
21
27
  if openscap_proxy_id
22
- mapping = { "server" => openscap_proxy.hostname, "port" => openscap_proxy.port }
28
+ mapping = { config.server_param => openscap_proxy.hostname, config.port_param => openscap_proxy.port }
23
29
  update_scap_client_lookup_values(pairs, model_match, mapping)
24
30
  else
25
31
  destroy_scap_client_lookup_values pairs
@@ -54,10 +60,6 @@ module ForemanOpenscap
54
60
  end
55
61
  end
56
62
 
57
- def find_scap_client
58
- Puppetclass.find_by(name: "foreman_scap_client")
59
- end
60
-
61
63
  def lookup_matcher(model_match)
62
64
  model_match == "fqdn" ? "#{model_match}=#{name}" : "#{model_match}=#{title}"
63
65
  end
@@ -65,11 +67,5 @@ module ForemanOpenscap
65
67
  def openscap_proxy_has_feature
66
68
  errors.add(:openscap_proxy_id, _("must have Openscap feature")) if openscap_proxy_id && !openscap_proxy.has_feature?("Openscap")
67
69
  end
68
-
69
- def scap_client_class_present
70
- if changed.include?('openscap_proxy_id') && self.respond_to?(:openscap_proxy_id) && openscap_proxy_id
71
- errors.add(:openscap_proxy_id, _("Puppet class 'foreman_scap_client' not found, make sure it is imported from Puppet master")) unless find_scap_client
72
- end
73
- end
74
70
  end
75
71
  end
@@ -16,18 +16,18 @@ module ForemanOpenscap
16
16
  has_many :assets, :through => :asset_policies, :as => :assetable, :dependent => :destroy
17
17
 
18
18
  scoped_search :on => :name, :complete_value => true
19
-
20
- SCAP_PUPPET_CLASS = 'foreman_scap_client'.freeze
21
- POLICIES_CLASS_PARAMETER = 'policies'.freeze
22
- SERVER_CLASS_PARAMETER = 'server'.freeze
23
- PORT_CLASS_PARAMETER = 'port'.freeze
24
-
25
19
  before_validation :update_period_attrs
26
20
 
27
- validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 255 }
28
- validate :ensure_needed_puppetclasses
21
+ def self.deploy_by_variants
22
+ %w[puppet ansible manual]
23
+ end
24
+
25
+ validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 255 },
26
+ :if => Proc.new { |policy| policy.should_validate?('Policy Attributes') }
29
27
  validates :period, :inclusion => { :in => %w[weekly monthly custom], :message => _('is not a valid value') },
30
28
  :if => Proc.new { |policy| policy.should_validate?('Schedule') }
29
+ validates :deploy_by, :inclusion => { :in => Policy.deploy_by_variants },
30
+ :if => Proc.new { |policy| policy.should_validate?('Deployment Options') }
31
31
 
32
32
  validates :scap_content_id, presence: true, if: Proc.new { |policy| policy.should_validate?('SCAP Content') }
33
33
  validate :matching_content_profile, if: Proc.new { |policy| policy.should_validate?('SCAP Content') }
@@ -97,7 +97,7 @@ module ForemanOpenscap
97
97
  end
98
98
 
99
99
  def steps
100
- base_steps = [N_('Create policy'), N_('SCAP Content'), N_('Schedule')]
100
+ base_steps = [N_('Deployment Options'), N_('Policy Attributes'), N_('SCAP Content'), N_('Schedule')]
101
101
  base_steps << N_('Locations') if SETTINGS[:locations_enabled]
102
102
  base_steps << N_('Organizations') if SETTINGS[:organizations_enabled]
103
103
  base_steps << N_('Hostgroups') # always be last.
@@ -123,6 +123,10 @@ module ForemanOpenscap
123
123
  current_step == steps.first
124
124
  end
125
125
 
126
+ def current_step?(step_name)
127
+ current_step == step_name
128
+ end
129
+
126
130
  def last_step?
127
131
  current_step == steps.last
128
132
  end
@@ -246,49 +250,6 @@ module ForemanOpenscap
246
250
  (Date::DAYS_INTO_WEEK.with_indifferent_access[weekday] + 1) % 7
247
251
  end
248
252
 
249
- def ensure_needed_puppetclasses
250
- unless puppetclass = Puppetclass.find_by(name: SCAP_PUPPET_CLASS)
251
- errors[:base] << _("Required Puppet class %{class} is not found, please ensure it imported first.") % { :class => SCAP_PUPPET_CLASS }
252
- return false
253
- end
254
-
255
- return false unless override_policies_param(puppetclass)
256
- return false unless override_port_param(puppetclass)
257
- return false unless override_server_param(puppetclass)
258
- end
259
-
260
- def override_policies_param(puppetclass)
261
- override_param(puppetclass, POLICIES_CLASS_PARAMETER) do |param|
262
- param.key_type = 'array'
263
- param.default_value = '<%= @host.policies_enc %>'
264
- end
265
- end
266
-
267
- def override_port_param(puppetclass)
268
- override_param puppetclass, PORT_CLASS_PARAMETER
269
- end
270
-
271
- def override_server_param(puppetclass)
272
- override_param puppetclass, SERVER_CLASS_PARAMETER
273
- end
274
-
275
- def override_param(puppetclass, param_name)
276
- unless param = puppetclass.class_params.find_by(key: param_name)
277
- errors[:base] << _("Puppet class %{class} does not have %{parameter} class parameter.") % { :class => SCAP_PUPPET_CLASS, :parameter => param_name }
278
- return
279
- end
280
-
281
- param.override = true
282
-
283
- yield param if block_given?
284
-
285
- if param.changed? && !param.save
286
- errors[:base] << _("%{parameter} class parameter for class %{class} could not be configured.") % { :class => SCAP_PUPPET_CLASS, :parameter => param_name }
287
- return
288
- end
289
- param
290
- end
291
-
292
253
  def cron_line_split
293
254
  cron_line.to_s.split(' ')
294
255
  end
@@ -329,13 +290,7 @@ module ForemanOpenscap
329
290
  end
330
291
 
331
292
  def assign_policy_to_hostgroups
332
- if hostgroups.any?
333
- puppetclass = find_scap_puppetclass
334
- hostgroups.each do |hostgroup|
335
- hostgroup.puppetclasses << puppetclass unless hostgroup.puppetclasses.include? puppetclass
336
- populate_overrides(puppetclass, hostgroup)
337
- end
338
- end
293
+ HostgroupOverrider.new(self).populate
339
294
  end
340
295
 
341
296
  def profile_for_scan
@@ -348,27 +303,6 @@ module ForemanOpenscap
348
303
  end
349
304
  end
350
305
 
351
- def find_scap_puppetclass
352
- Puppetclass.find_by(name: SCAP_PUPPET_CLASS)
353
- end
354
-
355
- def populate_overrides(puppetclass, hostgroup)
356
- puppetclass.class_params.where(:override => true).find_each do |override|
357
- next unless hostgroup.puppet_proxy && (url = hostgroup.puppet_proxy.url).present?
358
-
359
- case override.key
360
- when SERVER_CLASS_PARAMETER
361
- lookup_value = LookupValue.where(:match => "hostgroup=#{hostgroup.to_label}", :lookup_key_id => override.id).first_or_initialize
362
- puppet_proxy_fqdn = URI.parse(url).host
363
- lookup_value.update_attribute(:value, puppet_proxy_fqdn)
364
- when PORT_CLASS_PARAMETER
365
- lookup_value = LookupValue.where(:match => "hostgroup=#{hostgroup.to_label}", :lookup_key_id => override.id).first_or_initialize
366
- puppet_proxy_port = URI.parse(url).port
367
- lookup_value.update_attribute(:value, puppet_proxy_port)
368
- end
369
- end
370
- end
371
-
372
306
  def assign_ids(ids, class_name)
373
307
  new_assets = ids.reject { |id| id.respond_to?(:empty?) && id.empty? }.reduce([]) do |memo, id|
374
308
  memo << assets.where(:assetable_type => class_name, :assetable_id => id).first_or_initialize
@@ -0,0 +1,38 @@
1
+ module ForemanOpenscap
2
+ module ClientConfig
3
+ class Ansible < Base
4
+ delegate :ansible_role_name, :to => :constants
5
+
6
+ alias config_item_name ansible_role_name
7
+
8
+ def type
9
+ :ansible
10
+ end
11
+
12
+ def available?
13
+ defined?(ForemanAnsible)
14
+ end
15
+
16
+ def inline_help
17
+ {
18
+ :text => "Requires Ansible plugin, #{ansible_role_name} Ansible role and variables. This will assign the role to the hosts or selected hostgroups.<br>To deploy foreman_scap_client, ansible roles run needs to be triggered manually. Manual run is also required after any change to this policy.",
19
+ :replace_text => 'Ansible role',
20
+ :route_helper_method => :hash_for_ansible_roles_path
21
+ }
22
+ end
23
+
24
+ def constants
25
+ OpenStruct.new(
26
+ :server_param => 'foreman_scap_client_server',
27
+ :port_param => 'foreman_scap_client_port',
28
+ :policies_param => 'foreman_scap_client_policies',
29
+ :ansible_role_name => 'theforeman.foreman_scap_client',
30
+ :config_item_class_name => 'AnsibleRole',
31
+ :override_method_name => 'ansible_variables',
32
+ :msg_name => _('Ansible role'),
33
+ :lookup_key_plural_name => _('Ansible variables')
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ module ForemanOpenscap
2
+ module ClientConfig
3
+ class Base
4
+ delegate :server_param, :port_param, :policies_param, :config_item_name,
5
+ :config_item_class_name, :override_method_name, :msg_name,
6
+ :lookup_key_plural_name, :to => :constants
7
+
8
+ def type
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def inline_help
13
+ {
14
+ :text => '',
15
+ :replace_text => '',
16
+ :route_helper_method => nil
17
+ }
18
+ end
19
+
20
+ def managed_overrides?
21
+ true
22
+ end
23
+
24
+ def available?
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def constants
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def collection_method
33
+ constants.config_item_class_name&.pluralize&.underscore
34
+ end
35
+
36
+ def find_config_item(scope = config_item_class_name.constantize)
37
+ scope.find_by :name => config_item_name
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ module ForemanOpenscap
2
+ module ClientConfig
3
+ class Manual < Base
4
+ def type
5
+ :manual
6
+ end
7
+
8
+ def available?
9
+ true
10
+ end
11
+
12
+ def inline_help
13
+ {
14
+ :text => "This leaves the setup of the foreman_scap_client solely on the user. The policy still needs to be defined in order to link incoming ARF reports."
15
+ }
16
+ end
17
+
18
+ def constants
19
+ OpenStruct.new
20
+ end
21
+
22
+ def managed_overrides?
23
+ false
24
+ end
25
+ end
26
+ end
27
+ end