diff --git a/.rubocop.yml b/.rubocop.yml index 6a23fda61..14668aeb0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -21,3 +21,15 @@ Metrics/LineLength: Style/StringLiterals: Enabled: false + +Style/CollectionMethods: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false + +Rails/HttpPositionalArguments: + Enabled: false + +Style/EmptyMethod: + EnforcedStyle: expanded diff --git a/app/assets/javascripts/foreman_openscap/policy_edit.js b/app/assets/javascripts/foreman_openscap/policy_edit.js index e54831ca3..c051eb53a 100644 --- a/app/assets/javascripts/foreman_openscap/policy_edit.js +++ b/app/assets/javascripts/foreman_openscap/policy_edit.js @@ -13,6 +13,21 @@ function scap_content_selected(element){ }) } +function tailoring_file_selected(element) { + var attrs = attribute_hash(['tailoring_file_id']); + var url = $(element).attr('data-url'); + tfm.tools.showSpinner(); + $.ajax({ + data: attrs, + type: 'post', + url: url, + complete: function() { reloadOnAjaxComplete($(element));}, + success: function(request) { + $('#tailoring_file_profile_select').html(request); + } + }) +} + function previous_step(previous) { $('#policy_current_step').val(previous); $('#new_policy').submit(); diff --git a/app/controllers/api/v2/compliance/policies_controller.rb b/app/controllers/api/v2/compliance/policies_controller.rb index d8f992b25..3192f178b 100644 --- a/app/controllers/api/v2/compliance/policies_controller.rb +++ b/app/controllers/api/v2/compliance/policies_controller.rb @@ -4,7 +4,7 @@ class PoliciesController < ::Api::V2::BaseController include Foreman::Controller::SmartProxyAuth include Foreman::Controller::Parameters::PolicyApi - add_smart_proxy_filters :content, :features => 'Openscap' + add_smart_proxy_filters [:content, :tailoring], :features => 'Openscap' before_filter :find_resource, :except => %w(index create) @@ -46,6 +46,8 @@ def show param :day_of_month, Integer, :desc => N_('Policy schedule day of month (only if period == "monthly")') param :cron_line, String, :desc => N_('Policy schedule cron line (only if period == "custom")') param :hostgroup_ids, Array, :desc => N_('Apply policy to host groups') + param :tailoring_file_id, Integer, :desc => N_('Tailoring file ID') + param :tailoring_file_profile_id, Integer, :desc => N_('Tailoring file profile ID') param_group :taxonomies, ::Api::V2::BaseController end end @@ -83,6 +85,16 @@ def content :filename => @scap_content.original_filename end + api :GET, '/compliance/policies/:id/tailoring', N_("Show a policy's Tailoring file") + param :id, :identifier, :required => true + + def tailoring + @tailoring_file = @policy.tailoring_file + send_data @tailoring_file.scap_file, + :type => 'application/xml', + :filename => @tailoring_file.original_filename + end + private def find_resource not_found and return if params[:id].blank? @@ -91,7 +103,7 @@ def find_resource def action_permission case params[:action] - when 'content' + when 'content', 'tailoring' :view else super diff --git a/app/controllers/api/v2/compliance/tailoring_files_controller.rb b/app/controllers/api/v2/compliance/tailoring_files_controller.rb new file mode 100644 index 000000000..7df857c71 --- /dev/null +++ b/app/controllers/api/v2/compliance/tailoring_files_controller.rb @@ -0,0 +1,84 @@ +module Api::V2 + module Compliance + class TailoringFilesController < ::Api::V2::BaseController + include Foreman::Controller::Parameters::TailoringFile + before_filter :find_resource, :except => %w(index create) + + def resource_name + '::ForemanOpenscap::TailoringFile' + end + + def get_resource + instance_variable_get :"@tailoring_file" or fail 'no resource loaded' + end + + api :GET, '/compliance/tailoring_files', N_('List Tailoring files') + param_group :search_and_pagination, ::Api::V2::BaseController + + def index + @tailoring_files = resource_scope_for_index(:permission => :view_tailoring_files) + end + + api :GET, '/compliance/tailoring_files/:id/xml', N_('Show a Tailoring file as XML') + param :id, :identifier, :required => true + + def xml + send_data @tailoring_file.scap_file, + :type => 'application/xml', + :filename => @tailoring_file.original_filename || "#{@tailoring_file.name}.xml" + end + + api :GET, '/compliance/tailoring_files/:id', N_('Show a Tailoring file') + param :id, :identifier, :required => true + def show + end + + def_param_group :tailoring_file do + param :tailoring_file, Hash, :required => true, :action_aware => true do + param :name, String, :required => true, :desc => N_('Tailoring file name') + param :scap_file, String, :required => true, :desc => N_('XML containing tailoring file') + param :original_filename, String, :desc => N_('Original file name of the XML file') + param_group :taxonomies, ::Api::V2::BaseController + end + end + + api :POST, '/compliance/tailoring_files', N_('Create a Tailoring file') + param_group :tailoring_file, :as => :create + + def create + @tailoring_file = ForemanOpenscap::TailoringFile.new(tailoring_file_params) + process_response @tailoring_file.save + end + + api :PUT, '/compliance/tailoring_files/:id', N_('Update a Tailoring file') + param :id, :identifier, :required => true + param_group :tailoring_file + + def update + process_response @tailoring_file.update_attributes(tailoring_file_params) + end + + api :DELETE, '/compliance/tailoring_files/:id', N_('Deletes a Tailoring file') + param :id, :identifier, :required => true + + def destroy + process_response @tailoring_file.destroy + end + + private + def find_resource + not_found and return if params[:id].blank? + instance_variable_set("@tailoring_file", resource_scope.find(params[:id])) + end + + def action_permission + case params[:action] + when 'xml' + :view + else + super + end + end + end + end +end diff --git a/app/controllers/concerns/foreman/controller/parameters/policy_api.rb b/app/controllers/concerns/foreman/controller/parameters/policy_api.rb index 201355c6d..dbb544be8 100644 --- a/app/controllers/concerns/foreman/controller/parameters/policy_api.rb +++ b/app/controllers/concerns/foreman/controller/parameters/policy_api.rb @@ -4,8 +4,8 @@ module Foreman::Controller::Parameters::PolicyApi class_methods do def filter_params_list [:description, :name, :period, :scap_content_id, :scap_content_profile_id, - :weekday, :day_of_month, :cron_line, :location_ids => [], :organization_ids => [], - :hostgroup_ids => []] + :weekday, :day_of_month, :cron_line, :tailoring_file_id, :tailoring_file_profile_id, + :location_ids => [], :organization_ids => [], :hostgroup_ids => []] end def policy_params_filter diff --git a/app/controllers/concerns/foreman/controller/parameters/tailoring_file.rb b/app/controllers/concerns/foreman/controller/parameters/tailoring_file.rb new file mode 100644 index 000000000..1a4192227 --- /dev/null +++ b/app/controllers/concerns/foreman/controller/parameters/tailoring_file.rb @@ -0,0 +1,15 @@ +module Foreman::Controller::Parameters::TailoringFile + extend ActiveSupport::Concern + + class_methods do + def tailoring_file_params_filter + Foreman::ParameterFilter.new(::ForemanOpenscap::TailoringFile).tap do |filter| + filter.permit :name, :scap_file, :original_filename, :location_ids => [], :organization_ids => [] + end + end + end + + def tailoring_file_params + self.class.tailoring_file_params_filter.filter_params(params, parameter_filter_context) + end +end diff --git a/app/controllers/policies_controller.rb b/app/controllers/policies_controller.rb index 86a6c14c8..0fad9bce5 100644 --- a/app/controllers/policies_controller.rb +++ b/app/controllers/policies_controller.rb @@ -4,16 +4,16 @@ class PoliciesController < ApplicationController before_filter :find_by_id, :only => [:show, :edit, :update, :parse, :destroy] before_filter :find_multiple, :only => [:select_multiple_hosts, :update_multiple_hosts, :disassociate_multiple_hosts, :remove_policy_from_multiple_hosts] + before_filter :find_tailoring_file, :only => [:tailoring_file_selected] def model_of_controller ::ForemanOpenscap::Policy end def index - @policies = resource_base - .search_for(params[:search], :order => params[:order]) - .paginate(:page => params[:page], :per_page => params[:per_page]) - .includes(:scap_content, :scap_content_profile) + @policies = resource_base.search_for(params[:search], :order => params[:order]). + paginate(:page => params[:page], :per_page => params[:per_page]). + includes(:scap_content, :scap_content_profile, :tailoring_file) if @policies.empty? && ForemanOpenscap::ScapContent.unconfigured? redirect_to scap_contents_path end @@ -70,6 +70,11 @@ def scap_content_selected end end + def tailoring_file_selected + @policy ||= ::ForemanOpenscap::Policy.new + render :partial => 'tailoring_file_selected', :locals => { :policy => @policy, :tailoring_file => @tailoring_file } + end + def select_multiple_hosts; end def update_multiple_hosts @@ -104,6 +109,10 @@ def find_by_id @policy = resource_base.find(params[:id]) end + def find_tailoring_file + @tailoring_file = ForemanOpenscap::TailoringFile.find(params[:tailoring_file_id]) if params[:tailoring_file_id].present? + end + def find_multiple # Lets search by name or id and make sure one of them exists first if params[:host_ids].present? @@ -126,7 +135,7 @@ def find_multiple def action_permission case params[:action] - when 'parse' + when 'parse', 'tailoring_file_selected' :view else super diff --git a/app/controllers/tailoring_files_controller.rb b/app/controllers/tailoring_files_controller.rb new file mode 100644 index 000000000..3c14a52e6 --- /dev/null +++ b/app/controllers/tailoring_files_controller.rb @@ -0,0 +1,75 @@ +class TailoringFilesController < ApplicationController + include Foreman::Controller::AutoCompleteSearch + include Foreman::Controller::Parameters::TailoringFile + + before_filter :find_tailoring_file, :only => [:destroy, :update, :edit, :xml] + before_filter :handle_file_upload, :only => [:create, :update] + + def model_of_controller + ::ForemanOpenscap::TailoringFile + end + + def index + @tailoring_files = resource_base.search_for(params[:search], :order => params[:order]) + .paginate(:page => params[:page], :per_page => params[:per_page]) + end + + def new + @tailoring_file = ::ForemanOpenscap::TailoringFile.new + end + + def create + @tailoring_file = ForemanOpenscap::TailoringFile.new(tailoring_file_params) + if @tailoring_file.save + process_success + else + process_error + end + end + + def edit + end + + def update + if @tailoring_file.update_attributes(tailoring_file_params) + process_success + else + process_error + end + end + + def destroy + if @tailoring_file.destroy + process_success + else + process_error :object => @tailoring_file + end + end + + def xml + send_data @tailoring_file.scap_file, + :type => 'application/xml', + :filename => @tailoring_file.original_filename || "#{@tailoring_file.name}.xml" + end + + private + + def find_tailoring_file + @tailoring_file = resource_base.find(params[:id]) + end + + def handle_file_upload + return unless params[:tailoring_file] && raw_file = params[:tailoring_file][:scap_file] + params[:tailoring_file][:original_filename] = raw_file.original_filename + params[:tailoring_file][:scap_file] = raw_file.tempfile.read if raw_file.respond_to?(:tempfile) && raw_file.tempfile.respond_to?(:read) + end + + def action_permission + case params[:action] + when 'xml' + :view + else + super + end + end +end diff --git a/app/helpers/policies_helper.rb b/app/helpers/policies_helper.rb index 787251d42..eef888a1f 100644 --- a/app/helpers/policies_helper.rb +++ b/app/helpers/policies_helper.rb @@ -5,6 +5,14 @@ def profiles_selection return [] end + def policy_profile_from_scap_content(policy) + policy.scap_content_profile.nil? ? "Default" : policy.scap_content_profile.title + end + + def effective_policy_profile(policy) + policy.tailoring_file ? policy.tailoring_file_profile.title : policy_profile_from_scap_content(policy) + end + def scap_content_selector(form) scap_contents = ::ForemanOpenscap::ScapContent.authorized(:view_scap_contents).all if scap_contents.length > 1 @@ -38,6 +46,23 @@ def scap_content_profile_selector(form) end end + def tailoring_file_selector(form) + select_f form, :tailoring_file_id, ForemanOpenscap::TailoringFile.all.authorized(:view_tailoring_files), :id, :name, + { :include_blank => _('Choose Tailoring File') }, + { :label => _('Tailoring File'), + :onchange => 'tailoring_file_selected(this)', + :'data-url' => method_path('tailoring_file_selected') } + end + + def tailoring_file_profile_selector(form, tailoring_file) + if tailoring_file + select_f form, :tailoring_file_profile_id, tailoring_file.scap_content_profiles, :id, :title, + { :selected => tailoring_file.scap_content_profiles.first.id }, + { :label => _("XCCDF Profile in Tailoring File"), + :help_inline => _("This profile will be used to override the one from scap content") } + end + end + def submit_or_cancel_policy(form, overwrite = nil, args = { }) args[:cancel_path] ||= send("#{controller_name}_path") content_tag(:div, :class => "clearfix") do diff --git a/app/lib/proxy_api/openscap.rb b/app/lib/proxy_api/openscap.rb index 718a12b24..b6bde57e4 100644 --- a/app/lib/proxy_api/openscap.rb +++ b/app/lib/proxy_api/openscap.rb @@ -11,10 +11,17 @@ def fetch_policies_for_scap_content(scap_file) parse(post(scap_file, "scap_content/policies")) end - def validate_scap_content(scap_file) - parse(post(scap_file, "scap_content/validator")) + def fetch_profiles_for_tailoring_file(scap_file) + parse(post(scap_file, "tailoring_file/profiles")) + end + + def validate_scap_file(scap_file, type) + parse(post(scap_file, "scap_file/validator/#{type}")) rescue RestClient::RequestTimeout => e raise ::ProxyAPI::ProxyException.new(url, e, N_("Request timed out. Please try increasing Settings -> proxy_request_timeout")) + rescue RestClient::ResourceNotFound => e + raise ::ProxyAPI::ProxyException.new(url, e, + N_("Could not validate %s. Please make sure you have appropriate proxy version to use this functionality") % scap_file.class) end def policy_html_guide(scap_file, policy) diff --git a/app/models/concerns/foreman_openscap/data_stream_content.rb b/app/models/concerns/foreman_openscap/data_stream_content.rb new file mode 100644 index 000000000..e5dd0b7a8 --- /dev/null +++ b/app/models/concerns/foreman_openscap/data_stream_content.rb @@ -0,0 +1,43 @@ +module ForemanOpenscap + module DataStreamContent + require 'digest/sha2' + + extend ActiveSupport::Concern + + included do + validates :digest, :presence => true + validates :scap_file, :presence => true + + validates_with ForemanOpenscap::DataStreamValidator + + after_save :create_profiles + + before_validation :redigest, :if => lambda { |ds_content| ds_content.persisted? && ds_content.scap_file_changed? } + before_destroy ActiveRecord::Base::EnsureNotUsedBy.new(:policies) + end + + def proxy_url + @proxy_url ||= SmartProxy.with_features('Openscap').find do |proxy| + available = ProxyAPI::AvailableProxy.new(:url => proxy.url) + available.available? + end.try(:url) + @proxy_url + end + + def digest + self[:digest] ||= Digest::SHA256.hexdigest(scap_file.to_s) + end + + private + + def redigest + self[:digest] = Digest::SHA256.hexdigest(scap_file.to_s) + end + + def create_profiles + fetch_profiles.each do |key, title| + ScapContentProfile.where(:profile_id => key, :title => title, "#{self.class.to_s.demodulize.underscore}_id".to_sym => id).first_or_create + end + end + end +end diff --git a/app/models/foreman_openscap/policy.rb b/app/models/foreman_openscap/policy.rb index 09830adc5..01beaf700 100644 --- a/app/models/foreman_openscap/policy.rb +++ b/app/models/foreman_openscap/policy.rb @@ -6,6 +6,8 @@ class Policy < ActiveRecord::Base belongs_to :scap_content belongs_to :scap_content_profile + belongs_to :tailoring_file + belongs_to :tailoring_file_profile, :class_name => ForemanOpenscap::ScapContentProfile has_many :policy_arf_reports has_many :arf_reports, :through => :policy_arf_reports, :dependent => :destroy has_many :asset_policies @@ -28,7 +30,7 @@ class Policy < ActiveRecord::Base validates :scap_content_id, presence: true, if: Proc.new { |policy| policy.should_validate?('SCAP Content') } validates :scap_content_profile_id, presence: true, if: Proc.new { |policy| policy.should_validate?('SCAP Content') } - validate :valid_cron_line, :valid_weekday, :valid_day_of_month + validate :valid_cron_line, :valid_weekday, :valid_day_of_month, :valid_tailoring, :valid_tailoring_profile after_save :assign_policy_to_hostgroups # before_destroy - ensure that the policy has no hostgroups, or classes @@ -166,9 +168,11 @@ def unassign_hosts(hosts) def to_enc { 'id' => self.id, - 'profile_id' => self.scap_content_profile.try(:profile_id) || '', + 'profile_id' => profile_for_scan, 'content_path' => "/var/lib/openscap/content/#{self.scap_content.digest}.xml", - 'download_path' => "/compliance/policies/#{self.id}/content" # default to proxy path + 'tailoring_path' => tailoring_file ? "/var/lib/openscap/tailoring/#{self.tailoring_file.digest}.xml" : '', + 'download_path' => "/compliance/policies/#{self.id}/content", # default to proxy path + 'tailoring_download_path' => "/compliance/policies/#{self.id}/tailoring" }.merge(period_enc) end @@ -273,6 +277,17 @@ def valid_day_of_month end end + def valid_tailoring + errors.add(:tailoring_file_id, _("must be present when tailoring file profile present")) if tailoring_file_profile_id && !tailoring_file_id + errors.add(:tailoring_file_profile_id, _("must be present when tailoring file present")) if !tailoring_file_profile_id && tailoring_file_id + end + + def valid_tailoring_profile + if tailoring_file && tailoring_file_profile && !ScapContentProfile.where(:tailoring_file_id => tailoring_file_id).include?(tailoring_file_profile) + errors.add(:tailoring_file_profile, _("does not come from selected tailoring file")) + end + end + def assign_policy_to_hostgroups if hostgroups.any? puppetclass = find_scap_puppetclass @@ -283,6 +298,16 @@ def assign_policy_to_hostgroups end end + def profile_for_scan + if tailoring_file_profile + tailoring_file_profile.profile_id + elsif scap_content_profile + scap_content_profile.profile_id + else + '' + end + end + def find_scap_puppetclass Puppetclass.find_by_name(SCAP_PUPPET_CLASS) end diff --git a/app/models/foreman_openscap/scap_content.rb b/app/models/foreman_openscap/scap_content.rb index ca3c6d89b..e2586c3af 100644 --- a/app/models/foreman_openscap/scap_content.rb +++ b/app/models/foreman_openscap/scap_content.rb @@ -1,56 +1,13 @@ -require 'digest/sha2' - module ForemanOpenscap - class DataStreamValidator < ActiveModel::Validator - def validate(scap_content) - return unless scap_content.scap_file_changed? - - unless SmartProxy.with_features('Openscap').any? - scap_content.errors.add(:base, _('No proxy with OpenSCAP features')) - return false - end - - if scap_content.proxy_url.nil? - scap_content.errors.add(:base, _('No available proxy to validate SCAP content')) - return false - end - - begin - api = ProxyAPI::Openscap.new(:url => scap_content.proxy_url) - errors = api.validate_scap_content(scap_content.scap_file) - if errors && errors['errors'].any? - errors['errors'].each { |error| scap_content.errors.add(:scap_file, _(error)) } - return false - end - rescue *ProxyAPI::AvailableProxy::HTTP_ERRORS => e - scap_content.errors.add(:base, _('No available proxy to validate. Returned with error: %s') % e) - return false - end - - - unless (scap_content.scap_content_profiles.map(&:profile_id) - scap_content.fetch_profiles.keys).empty? - scap_content.errors.add(:scap_file, _('Changed file does not include existing SCAP content profiles')) - return false - end - end - end - class ScapContent < ActiveRecord::Base include Authorizable include Taxonomix + include DataStreamContent has_many :scap_content_profiles, :dependent => :destroy has_many :policies - before_destroy EnsureNotUsedBy.new(:policies) - - validates_with DataStreamValidator validates :title, :presence => true, :length => { :maximum => 255 } - validates :digest, :presence => true - validates :scap_file, :presence => true - - after_save :create_profiles - before_validation :redigest, :if => lambda { |scap_content| scap_content.persisted? && scap_content.scap_file_changed? } scoped_search :on => :title, :complete_value => true scoped_search :on => :original_filename, :complete_value => true, :rename => :filename @@ -77,8 +34,9 @@ def to_label title end - def digest - self[:digest] ||= Digest::SHA256.hexdigest "#{scap_file}" + def as_json(*args) + hash = super + hash["scap_file"] = scap_file.to_s.encode('utf-8', :invalid => :replace, :undef => :replace, :replace => '_') end def fetch_profiles @@ -86,31 +44,5 @@ def fetch_profiles profiles = api.fetch_policies_for_scap_content(scap_file) profiles end - - def proxy_url - @proxy_url ||= SmartProxy.with_features('Openscap').find do |proxy| - available = ProxyAPI::AvailableProxy.new(:url => proxy.url) - available.available? - end.try(:url) - @proxy_url - end - - def as_json(*args) - hash = super - hash["scap_file"] = scap_file.to_s.encode('utf-8', :invalid => :replace, :undef => :replace, :replace => '_') - end - - private - - def create_profiles - profiles = fetch_profiles - profiles.each {|key, title| - scap_content_profiles.where(:profile_id => key, :title => title).first_or_create - } - end - - def redigest - self[:digest] = Digest::SHA256.hexdigest "#{scap_file}" - end end end diff --git a/app/models/foreman_openscap/scap_content_profile.rb b/app/models/foreman_openscap/scap_content_profile.rb index 16551ceb2..8492caea9 100644 --- a/app/models/foreman_openscap/scap_content_profile.rb +++ b/app/models/foreman_openscap/scap_content_profile.rb @@ -2,5 +2,7 @@ module ForemanOpenscap class ScapContentProfile < ActiveRecord::Base belongs_to :scap_content has_many :policies + belongs_to :tailoring_file + has_many :tailoring_file_policies, :class_name => ForemanOpenscap::Policy end end diff --git a/app/models/foreman_openscap/tailoring_file.rb b/app/models/foreman_openscap/tailoring_file.rb new file mode 100644 index 000000000..b1aa382e5 --- /dev/null +++ b/app/models/foreman_openscap/tailoring_file.rb @@ -0,0 +1,19 @@ +module ForemanOpenscap + class TailoringFile < ActiveRecord::Base + include Authorizable + include Taxonomix + include DataStreamContent + + has_many :policies + has_many :scap_content_profiles, :dependent => :destroy + validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 255 } + + scoped_search :on => :name, :complete_value => true + scoped_search :on => :original_filename, :complete_value => true, :rename => :filename + + def fetch_profiles + api = ProxyAPI::Openscap.new(:url => proxy_url) + api.fetch_profiles_for_tailoring_file(scap_file) + end + end +end diff --git a/app/validators/foreman_openscap/data_stream_validator.rb b/app/validators/foreman_openscap/data_stream_validator.rb new file mode 100644 index 000000000..0ab8a117c --- /dev/null +++ b/app/validators/foreman_openscap/data_stream_validator.rb @@ -0,0 +1,44 @@ +module ForemanOpenscap + class DataStreamValidator < ActiveModel::Validator + def validate(data_stream_content) + return unless data_stream_content.scap_file_changed? + + content_type = data_type(data_stream_content) + + unless SmartProxy.with_features('Openscap').any? + data_stream_content.errors.add(:base, _('No proxy with OpenSCAP features')) + return false + end + + if data_stream_content.proxy_url.nil? + data_stream_content.errors.add(:base, _('No available proxy to validate SCAP data stream file')) + return false + end + + begin + api = ProxyAPI::Openscap.new(:url => data_stream_content.proxy_url) + errors = api.validate_scap_file(data_stream_content.scap_file, content_type) + if errors && errors['errors'].any? + errors['errors'].each { |error| data_stream_content.errors.add(:scap_file, _(error)) } + return false + end + rescue *ProxyAPI::AvailableProxy::HTTP_ERRORS => e + data_stream_content.errors.add(:base, _('No available proxy to validate. Returned with error: %s') % e) + return false + end + + is_scap_content = content_type == 'scap_content' + + if is_scap_content && !(data_stream_content.scap_content_profiles.map(&:profile_id) - data_stream_content.fetch_profiles.keys).empty? + data_stream_content.errors.add(:scap_file, _('Changed file does not include existing SCAP content profiles')) + return false + end + end + + private + + def data_type(data_stream_content) + data_stream_content.class.to_s.demodulize.underscore + end + end +end diff --git a/app/views/api/v2/compliance/policies/base.json.rabl b/app/views/api/v2/compliance/policies/base.json.rabl index 8b2573b1e..29ae470a5 100644 --- a/app/views/api/v2/compliance/policies/base.json.rabl +++ b/app/views/api/v2/compliance/policies/base.json.rabl @@ -3,4 +3,5 @@ object @policy extends "api/v2/compliance/common/org" extends "api/v2/compliance/common/loc" -attributes :id, :name, :period, :weekday, :description, :scap_content_id, :scap_content_profile_id, :day_of_month, :cron_line +attributes :id, :name, :period, :weekday, :description, :scap_content_id, :scap_content_profile_id, :day_of_month, :cron_line, + :tailoring_file_id, :tailoring_file_profile_id diff --git a/app/views/api/v2/compliance/tailoring_files/base.json.rabl b/app/views/api/v2/compliance/tailoring_files/base.json.rabl new file mode 100644 index 000000000..8bdbd99d3 --- /dev/null +++ b/app/views/api/v2/compliance/tailoring_files/base.json.rabl @@ -0,0 +1,6 @@ +object @tailoring_file + +extends "api/v2/compliance/common/org" +extends "api/v2/compliance/common/loc" + +attributes :id, :name, :original_filename, :digest diff --git a/app/views/api/v2/compliance/tailoring_files/index.json.rabl b/app/views/api/v2/compliance/tailoring_files/index.json.rabl new file mode 100644 index 000000000..bb6ec4b0c --- /dev/null +++ b/app/views/api/v2/compliance/tailoring_files/index.json.rabl @@ -0,0 +1,3 @@ +collection @tailoring_files + +extends "api/v2/compliance/tailoring_files/main" diff --git a/app/views/api/v2/compliance/tailoring_files/main.json.rabl b/app/views/api/v2/compliance/tailoring_files/main.json.rabl new file mode 100644 index 000000000..cd0e76cfc --- /dev/null +++ b/app/views/api/v2/compliance/tailoring_files/main.json.rabl @@ -0,0 +1,5 @@ +object @tailoring_file + +extends "api/v2/compliance/tailoring_files/base" + +attributes :created_at, :updated_at diff --git a/app/views/api/v2/compliance/tailoring_files/show.json.rabl b/app/views/api/v2/compliance/tailoring_files/show.json.rabl new file mode 100644 index 000000000..52dbfaa39 --- /dev/null +++ b/app/views/api/v2/compliance/tailoring_files/show.json.rabl @@ -0,0 +1,7 @@ +object @tailoring_file + +extends "api/v2/compliance/tailoring_files/main" + +child :scap_content_profiles => :tailoring_file_profiles do |profile| + attributes :id, :profile_id, :title +end diff --git a/app/views/policies/_form.html.erb b/app/views/policies/_form.html.erb index 24db89079..25a0ef605 100644 --- a/app/views/policies/_form.html.erb +++ b/app/views/policies/_form.html.erb @@ -26,6 +26,15 @@ <%= scap_content_profile_selector(f) %> + + <%= tailoring_file_selector(f) %> + + + <% if @policy.tailoring_file %> + <%= render 'tailoring_file_selected', :f => f, :policy => @policy, :tailoring_file => @policy.tailoring_file %> + <% end %> + +
<%= select_f(f, :period, %w[Weekly Monthly Custom], :downcase, :to_s, diff --git a/app/views/policies/_list.html.erb b/app/views/policies/_list.html.erb index a20fc4640..fd4dd2c52 100644 --- a/app/views/policies/_list.html.erb +++ b/app/views/policies/_list.html.erb @@ -1,8 +1,10 @@ - - - + + + + + <% for policy in @policies %> @@ -17,7 +19,17 @@ <% end %> + +
NameContentProfile<%= _('Name') %><%= _('Content') %><%= _('Profile') %><%= _('Tailoring File') %><%= _('Effective Profile') %>
- <%= policy.scap_content_profile.nil? ? "Default" : policy.scap_content_profile.title %> + <%= policy_profile_from_scap_content policy %> + + <% if policy.tailoring_file %> + <%= link_to_if_authorized policy.tailoring_file.name, hash_for_edit_tailoring_file_path(:id => policy.tailoring_file_id) %> + <% else %> + <%= _('None') %> + <% end%> + + <%= effective_policy_profile policy %> <%= action_buttons( diff --git a/app/views/policies/_tailoring_file_selected.html.erb b/app/views/policies/_tailoring_file_selected.html.erb new file mode 100644 index 000000000..c0a6b2e21 --- /dev/null +++ b/app/views/policies/_tailoring_file_selected.html.erb @@ -0,0 +1,3 @@ +<%= fields_for policy do |f| %> + <%= tailoring_file_profile_selector(f, tailoring_file) %> +<% end %> diff --git a/app/views/policies/steps/_scap_content_form.html.erb b/app/views/policies/steps/_scap_content_form.html.erb index d6c0ddb7a..df7c3aa87 100644 --- a/app/views/policies/steps/_scap_content_form.html.erb +++ b/app/views/policies/steps/_scap_content_form.html.erb @@ -5,5 +5,13 @@ <%= scap_content_profile_selector(f) %> + + <%= tailoring_file_selector(f) %> + + + <% if @policy.tailoring_file %> + <%= render 'tailoring_file_selected', :f => f, :policy => @policy, :tailoring_file => @policy.tailoring_file %> + <% end %> + diff --git a/app/views/tailoring_files/_form.html.erb b/app/views/tailoring_files/_form.html.erb new file mode 100644 index 000000000..a81057056 --- /dev/null +++ b/app/views/tailoring_files/_form.html.erb @@ -0,0 +1,25 @@ +<%= form_for @tailoring_file, + :url => (@tailoring_file.id? ? + tailoring_file_path(:id => @tailoring_file.id) : tailoring_files_path), + :html => { :multipart => true } do |f| %> + + <%= base_errors_for @tailoring_file %> + + +
+
+ <%= text_f(f, :name) %> + <%= file_field_f f, :scap_file, :help_block => _("Upload DataStream Tailoring file") %> +
+ <%= render 'taxonomies/loc_org_tabs', :f => f, :obj => @tailoring_file %> + <%= submit_or_cancel f %> +
+<% end %> diff --git a/app/views/tailoring_files/_list.html.erb b/app/views/tailoring_files/_list.html.erb new file mode 100644 index 000000000..eb9052e68 --- /dev/null +++ b/app/views/tailoring_files/_list.html.erb @@ -0,0 +1,25 @@ + + + + + + + <% @tailoring_files.each do |file| %> + + + + + + <% end %> +
<%= _('Name')%><%= _('Filename') %>
+ <%= file.name %> + + <%= file.original_filename %> + + <%= action_buttons( + display_link_if_authorized(_("Edit"), hash_for_edit_tailoring_file_path(:id => file.id)), + display_delete_if_authorized(hash_for_tailoring_file_path(:id => file.id), + :confirm => _("Delete tailoring file %s?") % file.name), + display_link_if_authorized(_("Download"), hash_for_xml_tailoring_file_path(:id => file.id), :data => { :no_turbolink => true }) + ) %> +
diff --git a/app/views/tailoring_files/edit.html.erb b/app/views/tailoring_files/edit.html.erb new file mode 100644 index 000000000..cf2727ebc --- /dev/null +++ b/app/views/tailoring_files/edit.html.erb @@ -0,0 +1,3 @@ +<% title _("Edit Tailoring File") %> + +<%= render :partial => 'form' %> diff --git a/app/views/tailoring_files/index.html.erb b/app/views/tailoring_files/index.html.erb new file mode 100644 index 000000000..ab49db656 --- /dev/null +++ b/app/views/tailoring_files/index.html.erb @@ -0,0 +1,3 @@ +<% title _("Tailoring Files") %> +<% title_actions(display_link_if_authorized(_("Upload New Tailoring file"), hash_for_new_tailoring_file_path, :class => 'btn btn-default')) %> +<%= render :partial => 'list' %> diff --git a/app/views/tailoring_files/new.html.erb b/app/views/tailoring_files/new.html.erb new file mode 100644 index 000000000..c57280642 --- /dev/null +++ b/app/views/tailoring_files/new.html.erb @@ -0,0 +1,3 @@ +<% title _("Upload new Tailoring File") %> + +<%= render :partial => 'form' %> diff --git a/app/views/tailoring_files/welcome.html.erb b/app/views/tailoring_files/welcome.html.erb new file mode 100644 index 000000000..327a4e45d --- /dev/null +++ b/app/views/tailoring_files/welcome.html.erb @@ -0,0 +1,15 @@ +<% content_for(:title, _("Tailoring Files")) %> +
+
+ <%= icon_text("key", "", :kind => "fa") %> +
+

<%= _('Tailoring Files') %>

+

<%= _('It may sometimes be required to adjust the security policy to your specific needs. ') %>
+ <%= (_('In Foreman, tailoring_files represent the custom modifications to default XCCDF profiles and they can be applied to hosts + via %s') % link_to('compliance policies', policies_path)).html_safe %> +

+ +
+ <%= new_link(_('New Tailoring File'), {}, { :class => "btn-lg" }) %> +
+
diff --git a/config/routes.rb b/config/routes.rb index fc90a4d2a..d3d7f9de5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,6 +25,7 @@ collection do get 'auto_complete_search' post 'scap_content_selected' + post 'tailoring_file_selected' get 'select_multiple_hosts' post 'update_multiple_hosts' get 'disassociate_multiple_hosts' @@ -38,6 +39,15 @@ end end + resources :tailoring_files, :except => [:show] do + member do + get 'xml' + end + collection do + get 'auto_complete_search' + end + end + resources :hosts, :only => [:show], :as => :compliance_hosts, :controller => :compliance_hosts end @@ -50,9 +60,15 @@ get 'xml' end end + resources :tailoring_files, :except => [:new, :edit] do + member do + get 'xml' + end + end resources :policies, :except => [:new, :edit] do member do get 'content' + get 'tailoring' end end resources :arf_reports, :only => [:index, :show, :destroy] do diff --git a/db/migrate/20161109155255_create_tailoring_files.rb b/db/migrate/20161109155255_create_tailoring_files.rb new file mode 100644 index 000000000..dcfa87d61 --- /dev/null +++ b/db/migrate/20161109155255_create_tailoring_files.rb @@ -0,0 +1,23 @@ +class CreateTailoringFiles < ActiveRecord::Migration + def up + create_table :foreman_openscap_tailoring_files do |t| + t.string :name, :unique => true, :null => false + t.text :scap_file + t.string :original_filename + t.datetime :created_at + t.datetime :updated_at + t.string :digest, :null => false + end + + add_column :foreman_openscap_policies, :tailoring_file_id, :integer, :references => :tailoring_file + add_column :foreman_openscap_policies, :tailoring_file_profile_id, :integer, :references => :scap_content_profile + add_column :foreman_openscap_scap_content_profiles, :tailoring_file_id, :integer, :references => :tailoring_file + end + + def down + remove_column :foreman_openscap_policies, :tailoring_file_id + remove_column :foreman_openscap_policies, :tailoring_file_profile_id + remove_column :foreman_openscap_scap_content_profiles, :tailoring_file_id + drop_table :foreman_openscap_tailoring_files + end +end diff --git a/lib/foreman_openscap/engine.rb b/lib/foreman_openscap/engine.rb index b9184c105..936e013ad 100644 --- a/lib/foreman_openscap/engine.rb +++ b/lib/foreman_openscap/engine.rb @@ -90,12 +90,27 @@ class Engine < ::Rails::Engine :resource_type => 'ForemanOpenscap::ScapContent' permission :edit_hosts, { :hosts => [:openscap_proxy_changed] }, :resource_type => "Host" permission :edit_hostgroups, { :hostgroups => [:openscap_proxy_changed] }, :resource_type => "Hostgroup" + permission :create_tailoring_files, { :tailoring_files => [:create, :new], + 'api/v2/compliance/tailoring_files' => [:create]}, + :resource_type => 'ForemanOpenscap::TailoringFile' + permission :view_tailoring_files, { :tailoring_files => [:index, :auto_complete_search, :xml], + :policies => [:tailoring_file_selected], + 'api/v2/compliance/tailoring_files' => [:show, :xml, :index], + 'api/v2/compliance/policies' => [:tailoring] }, + :resource_type => 'ForemanOpenscap::TailoringFile' + permission :edit_tailoring_files, { :tailoring_files => [:edit, :update], + 'api/v2/compliance/tailoring_files' => [:update] }, + :resource_type => 'ForemanOpenscap::TailoringFile' + permission :destroy_tailoring_files, { :tailoring_files => [:destroy], + 'api/v2/compliance/tailoring_files' => [:destroy] }, + :resource_type => 'ForemanOpenscap::TailoringFile' end - role "Compliance viewer", [:view_arf_reports, :view_policies, :view_scap_contents] + role "Compliance viewer", [:view_arf_reports, :view_policies, :view_scap_contents, :view_tailoring_files] role "Compliance manager", [:view_arf_reports, :view_policies, :view_scap_contents, :destroy_arf_reports, :edit_policies, :edit_scap_contents, :assign_policies, - :create_policies, :create_scap_contents, :destroy_policies, :destroy_scap_contents] + :create_policies, :create_scap_contents, :destroy_policies, :destroy_scap_contents, + :create_tailoring_files, :view_tailoring_files, :edit_tailoring_files, :destroy_tailoring_files] role "Create ARF report", [:create_arf_reports] # special as only Proxy can create #add menu entries @@ -109,6 +124,10 @@ class Engine < ::Rails::Engine menu :top_menu, :compliance_reports, :caption => N_('Reports'), :url_hash => {:controller => :arf_reports, :action => :index}, :parent => :hosts_menu + menu :top_menu, :compliance_files, :caption => N_('Tailoring Files'), + :url_hash => {:controller => :tailoring_files, :action => :index}, + :parent => :hosts_menu + # add dashboard widget widget 'compliance_host_reports_widget', diff --git a/test/factories/policy_factory.rb b/test/factories/policy_factory.rb index 2fe46a480..17cf6c635 100644 --- a/test/factories/policy_factory.rb +++ b/test/factories/policy_factory.rb @@ -5,6 +5,8 @@ weekday 'monday' scap_content scap_content_profile + tailoring_file nil + tailoring_file_profile nil day_of_month nil cron_line nil hosts [] diff --git a/test/factories/scap_content_related.rb b/test/factories/scap_content_related.rb index e388ee279..b400eb3cd 100644 --- a/test/factories/scap_content_related.rb +++ b/test/factories/scap_content_related.rb @@ -12,4 +12,11 @@ f.profile_id 'xccdf_org.test.common_test_profile' f.title 'test Profile for testing' end + + factory :tailoring_file, :class => ForemanOpenscap::TailoringFile do |f| + f.sequence(:name) { |n| "tailoring_file_#{n}" } + f.original_filename 'original tailoring filename' + f.scap_file { File.new("#{ForemanOpenscap::Engine.root}/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml", 'rb').read } + f.scap_content_profiles [] + end end diff --git a/test/files/tailoring_files/ssg-firefox-ds-tailoring-2.xml b/test/files/tailoring_files/ssg-firefox-ds-tailoring-2.xml new file mode 100644 index 000000000..c1b26944e --- /dev/null +++ b/test/files/tailoring_files/ssg-firefox-ds-tailoring-2.xml @@ -0,0 +1,23 @@ + + + + 1 + + Upstream Firefox STIG [CUSTOMIZED AGAIN] + This profile is developed under the DoD consensus model and DISA FSO Vendor STIG process, +serving as the upstream development environment for the Firefox STIG. + +As a result of the upstream/downstream relationship between the SCAP Security Guide project +and the official DISA FSO STIG baseline, users should expect variance between SSG and DISA FSO content. +For official DISA FSO STIG content, refer to http://iase.disa.mil/stigs/app-security/browser-guidance/Pages/index.aspx. + +While this profile is packaged by Red Hat as part of the SCAP Security Guide package, please note +that commercial support of this SCAP content is NOT available. This profile is provided as example +SCAP content with no endorsement for suitability or production readiness. Support for this +profile is provided by the upstream SCAP Security Guide community on a best-effort basis. The +upstream project homepage is https://fedorahosted.org/scap-security-guide/. + + + + + diff --git a/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml b/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml new file mode 100644 index 000000000..c8cc37ebf --- /dev/null +++ b/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml @@ -0,0 +1,31 @@ + + + + 1 + + Upstream Firefox STIG [CUSTOMIZED] + This profile is developed under the DoD consensus model and DISA FSO Vendor STIG process, +serving as the upstream development environment for the Firefox STIG. + +As a result of the upstream/downstream relationship between the SCAP Security Guide project +and the official DISA FSO STIG baseline, users should expect variance between SSG and DISA FSO content. +For official DISA FSO STIG content, refer to http://iase.disa.mil/stigs/app-security/browser-guidance/Pages/index.aspx. + +While this profile is packaged by Red Hat as part of the SCAP Security Guide package, please note +that commercial support of this SCAP content is NOT available. This profile is provided as example +SCAP content with no endorsement for suitability or production readiness. Support for this +profile is provided by the upstream SCAP Security Guide community on a best-effort basis. The +upstream project homepage is https://fedorahosted.org/scap-security-guide/. + + + + + + + + + + + + + diff --git a/test/functional/api/v2/compliance/policies_controller_test.rb b/test/functional/api/v2/compliance/policies_controller_test.rb index f9fbf18e1..7594dbcec 100644 --- a/test/functional/api/v2/compliance/policies_controller_test.rb +++ b/test/functional/api/v2/compliance/policies_controller_test.rb @@ -3,6 +3,12 @@ class Api::V2::Compliance::PoliciesControllerTest < ActionController::TestCase setup do ::ForemanOpenscap::Policy.any_instance.stubs(:ensure_needed_puppetclasses).returns(true) + @scap_content_profile = FactoryGirl.create(:scap_content_profile) + @attributes = { :policy => { :name => 'my_policy', + :scap_content_profile_id => @scap_content_profile.id, + :scap_content_id => @scap_content_profile.scap_content_id, + :period => 'weekly', + :weekday => 'friday' }} end test "should get index" do @@ -36,18 +42,30 @@ class Api::V2::Compliance::PoliciesControllerTest < ActionController::TestCase end test "should create a policy" do - scap_content_profile = FactoryGirl.create(:scap_content_profile) - attributes = { :policy => { :name => 'my_policy', - :scap_content_profile_id => scap_content_profile.id, - :scap_content_id => scap_content_profile.scap_content_id, - :period => 'weekly', - :weekday => 'friday' }} - post :create, attributes, set_session_user + post :create, @attributes, set_session_user response = ActiveSupport::JSON.decode(@response.body) - assert response['scap_content_profile_id'], scap_content_profile.to_param + assert response['scap_content_profile_id'], @scap_content_profile.to_param assert_response :created end + test "should not create a policy with tailoring file profile and without the actual file" do + tailoring_profile = FactoryGirl.create(:scap_content_profile, :profile_id => 'xccdf_org.test.tailoring_profile') + @attributes[:policy][:tailoring_file_profile_id] = tailoring_profile.id + post :create, @attributes, set_session_user + response = ActiveSupport::JSON.decode(@response.body) + assert_not_nil response['error']['errors']['tailoring_file_id'] + assert_response :unprocessable_entity + end + + test "should not create a policy with tailoring file and without tailoring profile" do + tailoring_file = FactoryGirl.create(:tailoring_file) + @attributes[:policy][:tailoring_file_id] = tailoring_file.id + post :create, @attributes, set_session_user + response = ActiveSupport::JSON.decode(@response.body) + assert_not_nil response['error']['errors']['tailoring_file_profile_id'] + assert_response :unprocessable_entity + end + test "should not create invalid policy" do post :create, {}, set_session_user assert_response :unprocessable_entity @@ -66,4 +84,13 @@ class Api::V2::Compliance::PoliciesControllerTest < ActionController::TestCase assert(@response.header['Content-Type'], 'application/xml') assert_response :success end + + test "should return xml of a tailoring file" do + tailoring_profile = FactoryGirl.create(:scap_content_profile) + policy = FactoryGirl.create(:policy, :tailoring_file => FactoryGirl.create(:tailoring_file, :scap_content_profiles => [tailoring_profile]), + :tailoring_file_profile => tailoring_profile) + get :tailoring, { :id => policy.id }, set_session_user + assert(@response.header['Content-Type'], 'application/xml') + assert_response :success + end end diff --git a/test/functional/api/v2/compliance/tailoring_files_controller_test.rb b/test/functional/api/v2/compliance/tailoring_files_controller_test.rb new file mode 100644 index 000000000..c7ace33bf --- /dev/null +++ b/test/functional/api/v2/compliance/tailoring_files_controller_test.rb @@ -0,0 +1,52 @@ +require 'test_plugin_helper' + +class Api::V2::Compliance::TailoringFilesControllerTest < ActionController::TestCase + + test "should get index" do + FactoryGirl.create(:tailoring_file) + get :index, {}, set_session_user + response = ActiveSupport::JSON.decode(@response.body) + assert response['results'].any? + assert_response :success + end + + test "should return xml of tailoring_file" do + tailoring_file = FactoryGirl.create(:tailoring_file) + get :show, { :id => tailoring_file.id }, set_session_user + assert(@response.header['Content-Type'], 'application/xml') + assert_response :success + end + + test "should not create invalid tailoring_file" do + post :create, {}, set_session_user + assert_response :unprocessable_entity + end + + test "should create tailoring_file" do + tf = FactoryGirl.build(:tailoring_file) + tf_params = { :name => tf.name, :original_filename => tf.original_filename, :scap_file => tf.scap_file } + post :create, tf_params, set_session_user + assert_response :success + end + + test "should update tailoring_file" do + tailoring_file = FactoryGirl.create(:tailoring_file) + put :update, { :id => tailoring_file.id, :tailoring_file => { :name => 'RHEL7 SCAP' }}, set_session_user + assert_response :success + assert tailoring_file.name, 'RHEL7 SCAP' + end + + test "should not update invalid tailoring_file" do + tailoring_file = FactoryGirl.create(:tailoring_file) + ProxyAPI::Openscap.any_instance.stubs(:validate_scap_file).returns({'errors' => ['Invalid file']}) + put :update, { :id => tailoring_file.id, :tailoring_file => { :scap_file => 'blah' }}, set_session_user + assert_response :unprocessable_entity + end + + test "should destory tailoring_file" do + tailoring_file = FactoryGirl.create(:tailoring_file) + delete :destroy, { :id => tailoring_file.id }, set_session_user + assert_response :ok + refute ForemanOpenscap::ScapContent.exists?(tailoring_file.id) + end +end diff --git a/test/functional/tailoring_files_controller_test.rb b/test/functional/tailoring_files_controller_test.rb new file mode 100644 index 000000000..449b67f10 --- /dev/null +++ b/test/functional/tailoring_files_controller_test.rb @@ -0,0 +1,38 @@ +require 'test_plugin_helper' + +class TailoringFilesControllerTest < ActionController::TestCase + setup do + @tailoring_file = FactoryGirl.create(:tailoring_file) + @scap_file = File.new("#{ForemanOpenscap::Engine.root}/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml", 'rb') + end + + test 'index' do + get :index, {}, set_session_user + assert_template 'index' + end + + test 'new' do + get :new, {}, set_session_user + assert_template 'new' + end + + test 'edit' do + get :edit, { :id => @tailoring_file.id }, set_session_user + assert_template 'edit' + end + + test 'create' do + uploaded_file = ActionDispatch::Http::UploadedFile.new(:tempfile => @scap_file, + :content_type => 'text/xml') + uploaded_file.original_filename = 'uploaded-tailoring-file.xml' + post :create, { :tailoring_file => { :name => 'some_file', :scap_file => uploaded_file } }, set_session_user + assert_redirected_to tailoring_files_url + end + + test 'destroy' do + tf = ForemanOpenscap::TailoringFile.first + delete :destroy, { :id => tf.id }, set_session_user + assert_redirected_to tailoring_files_url + refute ForemanOpenscap::TailoringFile.exists?(tf.id) + end +end diff --git a/test/test_plugin_helper.rb b/test/test_plugin_helper.rb index d15edbe71..96d8c5098 100644 --- a/test/test_plugin_helper.rb +++ b/test/test_plugin_helper.rb @@ -13,6 +13,22 @@ def skip_scap_callback end end +module ScapTestProxy + private + + def add_smart_proxy + FactoryGirl.create(:smart_proxy, :url => 'http://localhost:8443', :features => [FactoryGirl.create(:feature, :name => 'Openscap')]) + ProxyAPI::Features.any_instance.stubs(:features).returns(%w(puppet openscap)) + versions = { "version" => "1.11.0", "modules" => { "openscap" => "0.5.3" } } + ProxyAPI::Version.any_instance.stubs(:proxy_versions).returns(versions) + ProxyAPI::Openscap.any_instance.stubs(:validate_scap_file).returns({'errors' => []}) + ProxyAPI::Openscap.any_instance.stubs(:fetch_policies_for_scap_content). + returns({'xccdf_org.ssgproject.content_profile_common' => 'Common Profile for General-Purpose Fedora Systems'}) + ProxyAPI::Openscap.any_instance.stubs(:fetch_profiles_for_tailoring_file). + returns({'xccdf_org.ssgproject.test_profile_common' => 'Stubbed test profile'}) + end +end + class ActionMailer::TestCase include ScapClientPuppetclass setup :skip_scap_callback @@ -20,36 +36,14 @@ class ActionMailer::TestCase class ActionController::TestCase include ScapClientPuppetclass + include ScapTestProxy setup :add_smart_proxy, :skip_scap_callback - - private - - def add_smart_proxy - FactoryGirl.create(:smart_proxy, :url => 'http://localhost:8443', :features => [FactoryGirl.create(:feature, :name => 'Openscap')]) - ::ProxyAPI::Features.any_instance.stubs(:features).returns(%w(puppet openscap)) - versions = { "version" => "1.11.0", "modules" => { "openscap" => "0.5.3" } } - ::ProxyAPI::Version.any_instance.stubs(:proxy_versions).returns(versions) - ProxyAPI::Openscap.any_instance.stubs(:validate_scap_content).returns({'errors' => []}) - ProxyAPI::Openscap.any_instance.stubs(:fetch_policies_for_scap_content) - .returns({'xccdf_org.ssgproject.content_profile_common' => 'Common Profile for General-Purpose Fedora Systems'}) - end end class ActiveSupport::TestCase include ScapClientPuppetclass + include ScapTestProxy setup :add_smart_proxy, :skip_scap_callback - - private - - def add_smart_proxy - FactoryGirl.create(:smart_proxy, :url => 'http://localhost:8443', :features => [FactoryGirl.create(:feature, :name => 'Openscap')]) - ::ProxyAPI::Features.any_instance.stubs(:features).returns(%w(puppet openscap)) - versions = { "version" => "1.11.0", "modules" => { "openscap" => "0.5.3" } } - ::ProxyAPI::Version.any_instance.stubs(:proxy_versions).returns(versions) - ProxyAPI::Openscap.any_instance.stubs(:validate_scap_content).returns({'errors' => []}) - ProxyAPI::Openscap.any_instance.stubs(:fetch_policies_for_scap_content) - .returns({'xccdf_org.ssgproject.content_profile_common' => 'Common Profile for General-Purpose Fedora Systems'}) - end end diff --git a/test/unit/policy_test.rb b/test/unit/policy_test.rb index e3a122b0c..e2603b805 100644 --- a/test/unit/policy_test.rb +++ b/test/unit/policy_test.rb @@ -3,6 +3,8 @@ class PolicyTest < ActiveSupport::TestCase setup do ForemanOpenscap::Policy.any_instance.stubs(:ensure_needed_puppetclasses).returns(true) + ForemanOpenscap::DataStreamValidator.any_instance.stubs(:validate) + ForemanOpenscap::ScapContent.any_instance.stubs(:fetch_profiles).returns({ 'test_profile_key' => 'test_profile_title' }) @scap_content = FactoryGirl.create(:scap_content) @scap_profile = FactoryGirl.create(:scap_content_profile) end @@ -140,4 +142,28 @@ class PolicyTest < ActiveSupport::TestCase refute p.save assert p.errors[:scap_content_profile_id].include?("can't be blank") end + + test "should have correct scap profile in enc" do + p = FactoryGirl.create(:policy) + profile_id = p.scap_content_profile.profile_id + assert_equal profile_id, p.to_enc['profile_id'] + tailoring_profile = FactoryGirl.create(:scap_content_profile, :profile_id => 'xccdf_org.test.tailoring_test_profile') + p.tailoring_file_profile = tailoring_profile + assert_equal tailoring_profile.profile_id, p.to_enc['profile_id'] + end + + test "should not create policy with incorrect tailoring profile" do + tailoring_profile = FactoryGirl.create(:scap_content_profile, :profile_id => 'xccdf_org.test.common_tailoring_profile') + tailoring_file = FactoryGirl.create(:tailoring_file, :scap_content_profiles => [tailoring_profile]) + p = ForemanOpenscap::Policy.create(:name => "custom_policy", + :period => 'monthly', + :day_of_month => '5', + :scap_content => @scap_content, + :scap_content_profile => @scap_profile, + :tailoring_file => tailoring_file, + :tailoring_file_profile => @scap_profile) + refute p.valid? + p.tailoring_file_profile = tailoring_profile + assert p.save + end end diff --git a/test/unit/tailoring_file_test.rb b/test/unit/tailoring_file_test.rb new file mode 100644 index 000000000..ba8137cf5 --- /dev/null +++ b/test/unit/tailoring_file_test.rb @@ -0,0 +1,26 @@ +require 'test_plugin_helper' + +class TailoringFileTest < ActiveSupport::TestCase + setup do + @scap_file = File.new("#{ForemanOpenscap::Engine.root}/test/files/tailoring_files/ssg-firefox-ds-tailoring.xml", 'rb').read + end + + test 'should create tailoring file' do + tailoring_file = ForemanOpenscap::TailoringFile.create(:name => 'test_file', :scap_file => @scap_file, :original_filename => 'original name') + assert tailoring_file.valid? + end + + test 'should not create tailoring_file without scap file' do + tailoring_file = ForemanOpenscap::TailoringFile.create(:name => 'test_file', :original_filename => 'original name') + refute tailoring_file.valid? + end + + test 'should redigist when scap file changed' do + scap_file = File.new("#{ForemanOpenscap::Engine.root}/test/files/tailoring_files/ssg-firefox-ds-tailoring-2.xml", 'rb').read + tailoring_file = ForemanOpenscap::TailoringFile.create(:name => 'test_file', :scap_file => @scap_file, :original_filename => 'original name') + original_digest = tailoring_file.digest + tailoring_file.scap_file = scap_file + assert tailoring_file.save + refute_equal original_digest, tailoring_file.digest + end +end