diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 0a5f8aa4b..1737f64b1 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,16 +1,15 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2021-09-03 09:57:21 UTC using RuboCop version 1.19.1. +# on 2024-03-13 11:48:33 UTC using RuboCop version 1.23.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 3 +# Offense count: 2 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - - 'app/controllers/concerns/foreman/controller/parameters/oval_content.rb' - 'app/controllers/scap_contents_controller.rb' - 'app/controllers/tailoring_files_controller.rb' @@ -45,23 +44,21 @@ Lint/SendWithMixinArgument: Exclude: - 'lib/foreman_openscap/engine.rb' -# Offense count: 17 +# Offense count: 10 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'app/services/foreman_openscap/openscap_proxy_version_check.rb' - - 'app/services/foreman_openscap/oval/setup.rb' - 'app/views/api/v2/compliance/common/_loc.json.rabl' - 'app/views/api/v2/compliance/common/_org.json.rabl' - - 'app/views/api/v2/compliance/oval_policies/main.json.rabl' - 'app/views/api/v2/compliance/policies/children.json.rabl' - 'app/views/api/v2/compliance/scap_content_profiles/main.json.rabl' - 'app/views/api/v2/compliance/scap_contents/show.json.rabl' - 'app/views/api/v2/compliance/tailoring_files/show.json.rabl' - 'lib/foreman_openscap/engine.rb' -# Offense count: 42 +# Offense count: 34 # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. Lint/UnusedMethodArgument: @@ -72,10 +69,7 @@ Lint/UnusedMethodArgument: - 'app/models/concerns/foreman_openscap/compliance_status_scoped_search.rb' - 'app/models/concerns/foreman_openscap/host_extensions.rb' - 'app/models/foreman_openscap/compliance_status.rb' - - 'app/models/foreman_openscap/oval_status.rb' - 'app/services/foreman_openscap/lookup_key_overrides_common.rb' - - 'app/services/foreman_openscap/oval/configure.rb' - - 'app/services/foreman_openscap/oval/setup.rb' # Offense count: 6 Lint/UselessAssignment: @@ -98,14 +92,6 @@ Performance/RegexpMatch: - 'app/models/concerns/foreman_openscap/openscap_proxy_core_extensions.rb' - 'app/services/foreman_openscap/hostgroup_overrider_common.rb' -# Offense count: 13 -# Cop supports --auto-correct. -Rails/ContentTag: - Exclude: - - 'app/helpers/arf_reports_helper.rb' - - 'app/helpers/policies_helper.rb' - - 'app/helpers/policy_dashboard_helper.rb' - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforceForPrefixed. @@ -113,18 +99,15 @@ Rails/Delegate: Exclude: - 'app/models/foreman_openscap/asset.rb' -# Offense count: 18 +# Offense count: 17 # Configuration parameters: Include. # Include: app/models/**/*.rb Rails/HasManyOrHasOneDependent: Exclude: - - 'app/models/concerns/foreman_openscap/oval_facet_host_extensions.rb' + - 'app/models/concerns/foreman_openscap/host_extensions.rb' - 'app/models/concerns/foreman_openscap/smart_proxy_extensions.rb' - 'app/models/foreman_openscap/arf_report.rb' - 'app/models/foreman_openscap/asset.rb' - - 'app/models/foreman_openscap/cve.rb' - - 'app/models/foreman_openscap/oval_content.rb' - - 'app/models/foreman_openscap/oval_policy.rb' - 'app/models/foreman_openscap/policy.rb' - 'app/models/foreman_openscap/scap_content.rb' - 'app/models/foreman_openscap/scap_content_profile.rb' @@ -138,13 +121,12 @@ Rails/HttpStatus: Exclude: - 'app/controllers/api/v2/compliance/policies_controller.rb' -# Offense count: 7 +# Offense count: 6 # Configuration parameters: Include. # Include: app/models/**/*.rb Rails/InverseOf: Exclude: - 'app/models/concerns/foreman_openscap/host_extensions.rb' - - 'app/models/concerns/foreman_openscap/oval_facet_host_extensions.rb' - 'app/models/concerns/foreman_openscap/smart_proxy_extensions.rb' - 'app/models/foreman_openscap/arf_report.rb' @@ -157,6 +139,7 @@ Rails/LexicallyScopedActionFilter: - 'app/controllers/scap_contents_controller.rb' # Offense count: 2 +# Cop supports --auto-correct. # Configuration parameters: Include. # Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb Rails/Output: @@ -178,6 +161,3 @@ Rails/ReadWriteAttribute: Rails/TimeZone: Exclude: - 'app/models/foreman_openscap/arf_report.rb' - -Rails/HasManyOrHasOneDependent: - Enabled: false diff --git a/README.md b/README.md index 054ae26b2..5ddfdae4e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ of Foreman based infrastructure. + Waive known issues (one-time waivers, re-occurring, waivers) + Ad-hoc audit of given machine + Support for PreupgradeAssistant evaluation - + Vulnerability Assessment (processing OVAL CVE streams) + E-mail notifications ## Usage diff --git a/app/controllers/api/v2/compliance/oval_contents_controller.rb b/app/controllers/api/v2/compliance/oval_contents_controller.rb deleted file mode 100644 index 8eff3b090..000000000 --- a/app/controllers/api/v2/compliance/oval_contents_controller.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Api::V2 - module Compliance - class OvalContentsController < ::Api::V2::BaseController - include Foreman::Controller::Parameters::OvalContent - include ForemanOpenscap::Api::V2::ScapApiControllerExtensions - - before_action :find_resource, :except => %w[index create sync] - skip_before_action :check_media_type, :only => %w[create update] - - api :GET, '/compliance/oval_contents', N_('List OVAL contents') - param_group :search_and_pagination, ::Api::V2::BaseController - add_scoped_search_description_for(::ForemanOpenscap::OvalContent) - - def index - @oval_contents = resource_scope_for_index(:permission => :view_oval_contents) - end - - api :GET, '/compliance/oval_contents/:id', N_('Show an OVAL content') - param :id, :identifier, :required => true - - def show - end - - def_param_group :oval_content do - param :oval_content, Hash, :required => true, :action_aware => true do - param :name, String, :required => true, :desc => N_('OVAL content name') - param :scap_file, File, :desc => N_('XML containing OVAL content') - param :original_filename, String, :desc => N_('Original file name of the OVAL content file') - param :url, String, :desc => N_('URL of the OVAL content file') - param_group :taxonomies, ::Api::V2::BaseController - end - end - - api :POST, '/compliance/oval_contents', N_('Create OVAL content') - param_group :oval_content, :as => :create - - def create - @oval_content = ForemanOpenscap::OvalContent.new(oval_content_params) - process_response @oval_content.save - end - - api :PUT, '/compliance/oval_contents/:id', N_('Update an OVAL content') - param :id, :identifier, :required => true - param_group :oval_content - - def update - process_response @oval_content.update(oval_content_params) - end - - api :DELETE, '/compliance/oval_contents/:id', N_('Deletes an OVAL content') - param :id, :identifier, :required => true - - def destroy - process_response @oval_content.destroy - end - - api :POST, '/compliance/oval_contents/sync', N_('Sync contents that have remote source URL') - def sync - @oval_contents = ForemanOpenscap::Oval::SyncOvalContents.new.sync_all - end - - def action_permission - case params[:action] - when 'sync' - :update - else - super - end - end - end - end -end diff --git a/app/controllers/api/v2/compliance/oval_policies_controller.rb b/app/controllers/api/v2/compliance/oval_policies_controller.rb deleted file mode 100644 index dd5c13978..000000000 --- a/app/controllers/api/v2/compliance/oval_policies_controller.rb +++ /dev/null @@ -1,111 +0,0 @@ -module Api::V2 - module Compliance - class OvalPoliciesController < ::Api::V2::BaseController - include Foreman::Controller::SmartProxyAuth - include ForemanOpenscap::Api::V2::ScapApiControllerExtensions - include Foreman::Controller::Parameters::OvalPolicy - - add_smart_proxy_filters %i[oval_content], :features => 'Openscap' - - before_action :find_resource, :except => %w[index create] - skip_after_action :log_response_body, :only => %i[oval_content] - - api :GET, '/compliance/oval_policies', N_('List OVAL Policies') - param_group :search_and_pagination, ::Api::V2::BaseController - - def index - @oval_policies = resource_scope_for_index(:permission => :view_oval_policies) - end - - api :GET, '/compliance/oval_policies/:id', N_('Show an OVAL Policy') - param :id, :identifier, :required => true - - def show - end - - def_param_group :oval_policy do - param :oval_policy, Hash, :required => true, :action_aware => true do - param :name, String, :required => true, :desc => N_('OVAL Policy name') - param :oval_content_id, Integer, :required => true, :desc => N_('Policy OVAL content ID') - param :description, String, :desc => N_('OVAL Policy description') - param :period, String, :desc => N_('OVAL Policy schedule period (weekly, monthly, custom)') - param :weekday, String, :desc => N_('OVAL Policy schedule weekday (only if period == "weekly")') - param :day_of_month, Integer, :desc => N_('OVAL Policy schedule day of month (only if period == "monthly")') - param :cron_line, String, :desc => N_('OVAL Policy schedule cron line (only if period == "custom")') - param_group :taxonomies, ::Api::V2::BaseController - end - end - - api :POST, '/compliance/oval_policies', N_('Create an OVAL Policy') - param_group :oval_policy, :as => :create - - def create - @oval_policy = ForemanOpenscap::OvalPolicy.new(oval_policy_params) - process_response(@oval_policy.save) - end - - api :PUT, '/compliance/oval_policies/:id', N_('Update an OVAL Policy') - param :id, :identifier, :required => true - param_group :oval_policy - - def update - process_response(@oval_policy.update(oval_policy_params)) - end - - api :DELETE, '/compliance/oval_policies/:id', N_('Delete an OVAL Policy') - param :id, :identifier, :required => true - - def destroy - process_response @oval_policy.destroy - end - - api :POST, '/compliance/oval_policies/:id/assign_hostgroups', N_('Assign hostgroups to an OVAL Policy') - param :id, :identifier, :required => true - param :hostgroup_ids, Array, :desc => N_('Array of hostgroup IDs') - - def assign_hostgroups - assign _('hostgroups'), params["hostgroup_ids"], ::Hostgroup - end - - api :POST, '/compliance/oval_policies/:id/assign_hosts', N_('Assign hosts to an OVAL Policy') - param :id, :identifier, :required => true - param :host_ids, Array, :desc => N_('Array of host IDs') - - def assign_hosts - assign _('hosts'), params["host_ids"], ::Host::Managed - end - - api :GET, '/compliance/oval_policies/:id/oval_content', N_("Show a policy's OVAL content") - param :id, :identifier, :required => true - - def oval_content - @oval_content = @oval_policy.oval_content - send_data @oval_content.scap_file, - :type => 'application/x-bzip2', - :filename => @oval_content.original_filename - end - - def action_permission - case params[:action] - when 'assign_hostgroups', 'assign_hosts' - :edit - when 'oval_content' - :show - else - super - end - end - - private - - def assign(resource_plural, ids, model_class) - check_collection = ::ForemanOpenscap::Oval::Configure.new.assign(@oval_policy, ids, model_class) - if check_collection.all_passed? - render :json => { :message => (_("OVAL policy successfully configured with %s.") % resource_plural) } - else - render :json => { :results => check_collection.find_failed.map(&:to_h) } - end - end - end - end -end diff --git a/app/controllers/api/v2/compliance/oval_reports_controller.rb b/app/controllers/api/v2/compliance/oval_reports_controller.rb deleted file mode 100644 index f8e5ae3bf..000000000 --- a/app/controllers/api/v2/compliance/oval_reports_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Api - module V2 - module Compliance - class OvalReportsController < ::Api::V2::BaseController - include Foreman::Controller::SmartProxyAuth - add_smart_proxy_filters :create, :features => 'Openscap' - - skip_before_action :setup_has_many_params - before_action :find_resources_before_create, :only => [:create] - - api :POST, "/compliance/oval_reports/:cname/:oval_policy_id/:date", N_("Upload an OVAL report - a list of CVEs for given host") - param :cname, :identifier, :required => true - param :oval_policy_id, :identifier, :required => true - param :date, :identifier, :required => true - - def create - ForemanOpenscap::Oval::Cves.new.create(@host, params.to_unsafe_h) - if @host.errors.any? - upload_fail @host.errors.full_messages - else - @host.refresh_statuses([ForemanOpenscap::OvalStatus]) - render :json => { :result => :ok } - end - end - - private - - def find_resources_before_create - @host = ForemanOpenscap::Helper.find_host_by_name_or_uuid params[:cname] - - unless @host - upload_fail(_('Could not find host identified by: %s') % params[:cname]) - return - end - end - - def upload_fail(msg) - logger.error msg - render :json => { :result => :fail, :errors => msg }, :status => :unprocessable_entity - end - - def find_resource - end - end - end - end -end diff --git a/app/controllers/concerns/foreman/controller/parameters/oval_content.rb b/app/controllers/concerns/foreman/controller/parameters/oval_content.rb deleted file mode 100644 index 8c8665b75..000000000 --- a/app/controllers/concerns/foreman/controller/parameters/oval_content.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Foreman::Controller::Parameters::OvalContent - extend ActiveSupport::Concern - - class_methods do - def oval_content_params_filter - Foreman::ParameterFilter.new(::ForemanOpenscap::OvalContent).tap do |filter| - filter.permit :original_filename, :scap_file, :name, :url, :location_ids => [], :organization_ids => [] - end - end - end - - def oval_content_params - read_file_content self.class.oval_content_params_filter.filter_params(params, parameter_filter_context) - end - - def read_file_content(params) - return params unless file = params[:scap_file] - content = file.read - filename = file.original_filename - params.merge(:scap_file => content, :original_filename => params[:original_filename] || filename) - end -end diff --git a/app/controllers/concerns/foreman/controller/parameters/oval_policy.rb b/app/controllers/concerns/foreman/controller/parameters/oval_policy.rb deleted file mode 100644 index ac0b5448b..000000000 --- a/app/controllers/concerns/foreman/controller/parameters/oval_policy.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Foreman::Controller::Parameters::OvalPolicy - extend ActiveSupport::Concern - - class_methods do - def filter_params_list - [:description, :name, :period, - :weekday, :day_of_month, :cron_line, - :oval_content_id, - :location_ids => [], :organization_ids => []] - end - - def oval_policy_params_filter - Foreman::ParameterFilter.new(::ForemanOpenscap::OvalPolicy).tap do |filter| - filter.permit filter_params_list - end - end - end - - def oval_policy_params - self.class.oval_policy_params_filter.filter_params(params, parameter_filter_context) - end -end diff --git a/app/graphql/mutations/oval_contents/delete.rb b/app/graphql/mutations/oval_contents/delete.rb deleted file mode 100644 index 5a591aae1..000000000 --- a/app/graphql/mutations/oval_contents/delete.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Mutations - module OvalContents - class Delete < DeleteMutation - graphql_name 'DeleteOvalContentMutation' - description 'Deletes an OVAL Content' - resource_class ::ForemanOpenscap::OvalContent - end - end -end diff --git a/app/graphql/mutations/oval_policies/create.rb b/app/graphql/mutations/oval_policies/create.rb deleted file mode 100644 index 88660616f..000000000 --- a/app/graphql/mutations/oval_policies/create.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Mutations - module OvalPolicies - class Create < ::Mutations::BaseMutation - description 'Creates a new OVAL Policy' - graphql_name 'CreateOvalPolicyMutation' - - resource_class ::ForemanOpenscap::OvalPolicy - - argument :name, String - argument :description, String, required: false - argument :period, String - argument :weekday, String, required: false - argument :day_of_month, Integer, required: false - argument :cron_line, String, required: false - argument :oval_content_id, Integer, required: true - argument :hostgroup_ids, [Integer], required: false - - field :oval_policy, Types::OvalPolicy, 'The new OVAL Policy.', null: true - field :check_collection, [Types::OvalCheck], 'A collection of checks to detect OVAL policy configuration error', null: false - - def resolve(hostgroup_ids:, **params) - policy = ::ForemanOpenscap::OvalPolicy.new params - validate_object(policy) - authorize!(policy, :create) - check_collection = ::ForemanOpenscap::Oval::Configure.new.assign(policy, hostgroup_ids, ::Hostgroup) - { - :oval_policy => policy, - :check_collection => check_collection.checks - } - end - end - end -end diff --git a/app/graphql/mutations/oval_policies/delete.rb b/app/graphql/mutations/oval_policies/delete.rb deleted file mode 100644 index ffa61390a..000000000 --- a/app/graphql/mutations/oval_policies/delete.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Mutations - module OvalPolicies - class Delete < DeleteMutation - graphql_name 'DeleteOvalPolicyMutation' - description 'Deletes an OVAL Policy' - resource_class ::ForemanOpenscap::OvalPolicy - end - end -end diff --git a/app/graphql/mutations/oval_policies/update.rb b/app/graphql/mutations/oval_policies/update.rb deleted file mode 100644 index d5184c393..000000000 --- a/app/graphql/mutations/oval_policies/update.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Mutations - module OvalPolicies - class Update < UpdateMutation - graphql_name 'UpdateOvalPolicyMutation' - description 'Updates an OVAL Policy' - resource_class ::ForemanOpenscap::OvalPolicy - - argument :name, String, required: false - argument :description, String, required: false - argument :cron_line, String, required: false - - field :oval_policy, ::Types::OvalPolicy, 'The OVAL policy.', null: true - end - end -end diff --git a/app/graphql/types/cve.rb b/app/graphql/types/cve.rb deleted file mode 100644 index 726c573fb..000000000 --- a/app/graphql/types/cve.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Types - class Cve < BaseObject - description 'A CVE' - model_class ::ForemanOpenscap::Cve - - global_id_field :id - field :ref_id, String - field :ref_url, String - field :has_errata, Boolean - field :definition_id, String - has_many :hosts, Types::Host - - def self.graphql_definition - super.tap { |type| type.instance_variable_set(:@name, 'ForemanOpenscap::Cve') } - end - end -end diff --git a/app/graphql/types/oval_check.rb b/app/graphql/types/oval_check.rb deleted file mode 100644 index 3f72368d8..000000000 --- a/app/graphql/types/oval_check.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Types - class OvalCheck < GraphQL::Schema::Object - description 'A check that contains information about whether a particual prerequisite for OVAL policy deployment is configured correctly' - - field :id, String, null: false - field :title, String, null: false - field :fail_msg, String, null: true - field :errors, ::Types::RawJson, null: true - field :result, String, null: false - end -end diff --git a/app/graphql/types/oval_content.rb b/app/graphql/types/oval_content.rb deleted file mode 100644 index 1fc2a2ee0..000000000 --- a/app/graphql/types/oval_content.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Types - class OvalContent < BaseObject - description 'An OVAL Content' - model_class ::ForemanOpenscap::OvalContent - - include ::Types::Concerns::MetaField - - global_id_field :id - timestamps - field :name, String - field :digest, String - field :original_filename, String - field :url, String - - def self.graphql_definition - super.tap { |type| type.instance_variable_set(:@name, 'ForemanOpenscap::OvalContent') } - end - end -end diff --git a/app/graphql/types/oval_policy.rb b/app/graphql/types/oval_policy.rb deleted file mode 100644 index abb4a731a..000000000 --- a/app/graphql/types/oval_policy.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Types - class OvalPolicy < BaseObject - description 'An OVAL Policy' - model_class ::ForemanOpenscap::OvalPolicy - - include ::Types::Concerns::MetaField - - global_id_field :id - timestamps - field :name, String - field :description, String - field :period, String - field :weekday, String - field :day_of_month, String - field :cron_line, String - belongs_to :oval_content, ::Types::OvalContent - - has_many :hostgroups, ::Types::Hostgroup - - def self.graphql_definition - super.tap { |type| type.instance_variable_set(:@name, 'ForemanOpenscap::OvalPolicy') } - end - end -end diff --git a/app/models/concerns/foreman_openscap/oval_facet_host_extensions.rb b/app/models/concerns/foreman_openscap/oval_facet_host_extensions.rb deleted file mode 100644 index b5ab152f0..000000000 --- a/app/models/concerns/foreman_openscap/oval_facet_host_extensions.rb +++ /dev/null @@ -1,38 +0,0 @@ -module ForemanOpenscap - module OvalFacetHostExtensions - extend ActiveSupport::Concern - - ::Host::Managed::Jail.allow :oval_policies_enc, :oval_policies_enc_raw, :cves, :cves_without_errata - - included do - has_many :oval_policies, :through => :oval_facet, :class_name => 'ForemanOpenscap::OvalPolicy' - - has_many :host_cves, :class_name => 'ForemanOpenscap::HostCve', :foreign_key => :host_id - has_many :cves, :through => :host_cves, :class_name => 'ForemanOpenscap::Cve', :source => :cve - - scoped_search :relation => :host_cves, :on => :cve_id, :rename => :cve_id, :complete_value => false - end - - def cves_without_errata - cves.where(:has_errata => false) - end - - def cves_with_errata - cves.where(:has_errata => true) - end - - def combined_oval_policies - combined = oval_policies - combined += hostgroup.oval_policies + hostgroup.inherited_oval_policies if hostgroup - combined.uniq - end - - def oval_policies_enc_raw - combined_oval_policies.map(&:to_enc) - end - - def oval_policies_enc - oval_policies_enc_raw.to_json - end - end -end diff --git a/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb b/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb deleted file mode 100644 index ecb36cd9e..000000000 --- a/app/models/concerns/foreman_openscap/oval_facet_hostgroup_extensions.rb +++ /dev/null @@ -1,31 +0,0 @@ -module ForemanOpenscap - module OvalFacetHostgroupExtensions - extend ActiveSupport::Concern - - include InheritedPolicies - - included do - has_many :oval_policies, :through => :oval_facet, :class_name => 'ForemanOpenscap::OvalPolicy' - - scoped_search :relation => :oval_policies, - :on => :id, - :rename => :oval_policy_id, - :complete_value => false, - :only_explicit => true, - :ext_method => :find_by_oval_policy_id, - :operators => ['= '] - end - - def inherited_oval_policies - find_inherited_policies :oval_policies - end - - module ClassMethods - def find_by_oval_policy_id(_key, operator, value) - conditions = sanitize_sql_for_conditions(["#{::ForemanOpenscap::HostgroupOvalFacetOvalPolicy.table_name}.oval_policy_id #{operator} ?", value]) - hg_ids = ::ForemanOpenscap::Hostgroup::OvalFacet.joins(:hostgroup_oval_facet_oval_policies).where(conditions).pluck(:hostgroup_id) - { :conditions => ::Hostgroup.arel_table[:id].in(hg_ids).to_sql } - end - end - end -end diff --git a/app/models/foreman_openscap/cve.rb b/app/models/foreman_openscap/cve.rb deleted file mode 100644 index 7e778884c..000000000 --- a/app/models/foreman_openscap/cve.rb +++ /dev/null @@ -1,23 +0,0 @@ -module ForemanOpenscap - class Cve < ApplicationRecord - has_many :host_cves - has_many :hosts, :through => :host_cves - has_many :oval_policies, :through => :host_cves - - scoped_search :relation => :host_cves, :on => :oval_policy_id, :rename => :oval_policy_id, :complete_value => false - - scope :of_oval_policy, ->(policy_id) { - joins(:host_cves).where(:foreman_openscap_host_cves => { :oval_policy_id => policy_id }) - } - - scope :of_host, ->(host_id) { - joins(:host_cves).where(:foreman_openscap_host_cves => { :host_id => host_id }) - } - - validates :ref_id, :ref_url, :definition_id, :presence => true - - class Jail < ::Safemode::Jail - allow :ref_id, :ref_url - end - end -end diff --git a/app/models/foreman_openscap/host/oval_facet.rb b/app/models/foreman_openscap/host/oval_facet.rb deleted file mode 100644 index 7b5b5fca3..000000000 --- a/app/models/foreman_openscap/host/oval_facet.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ForemanOpenscap - module Host - class OvalFacet < ApplicationRecord - self.table_name = 'foreman_openscap_oval_facets' - - include Facets::Base - - validates :host, :presence => true, :allow_blank => false - - has_many :oval_facet_oval_policies, :dependent => :destroy, :class_name => 'ForemanOpenscap::OvalFacetOvalPolicy' - has_many :oval_policies, :through => :oval_facet_oval_policies, :class_name => 'ForemanOpenscap::OvalPolicy' - end - end -end diff --git a/app/models/foreman_openscap/host_cve.rb b/app/models/foreman_openscap/host_cve.rb deleted file mode 100644 index 572c6f97c..000000000 --- a/app/models/foreman_openscap/host_cve.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ForemanOpenscap - class HostCve < ApplicationRecord - belongs_to_host - belongs_to :cve - belongs_to :oval_policy - end -end diff --git a/app/models/foreman_openscap/hostgroup/oval_facet.rb b/app/models/foreman_openscap/hostgroup/oval_facet.rb deleted file mode 100644 index 5315cdb53..000000000 --- a/app/models/foreman_openscap/hostgroup/oval_facet.rb +++ /dev/null @@ -1,14 +0,0 @@ -module ForemanOpenscap - module Hostgroup - class OvalFacet < ApplicationRecord - self.table_name = 'foreman_openscap_hostgroup_oval_facets' - - include Facets::HostgroupFacet - - validates :hostgroup, :presence => true, :allow_blank => false - - has_many :hostgroup_oval_facet_oval_policies, :dependent => :destroy, :class_name => 'ForemanOpenscap::HostgroupOvalFacetOvalPolicy' - has_many :oval_policies, :through => :hostgroup_oval_facet_oval_policies, :class_name => 'ForemanOpenscap::OvalPolicy' - end - end -end diff --git a/app/models/foreman_openscap/hostgroup_oval_facet_oval_policy.rb b/app/models/foreman_openscap/hostgroup_oval_facet_oval_policy.rb deleted file mode 100644 index 0ecb137ca..000000000 --- a/app/models/foreman_openscap/hostgroup_oval_facet_oval_policy.rb +++ /dev/null @@ -1,6 +0,0 @@ -module ForemanOpenscap - class HostgroupOvalFacetOvalPolicy < ApplicationRecord - belongs_to :oval_policy - belongs_to :oval_facet, :class_name => 'ForemanOpenscap::Hostgroup::OvalFacet' - end -end diff --git a/app/models/foreman_openscap/oval_content.rb b/app/models/foreman_openscap/oval_content.rb deleted file mode 100644 index 5b344d2bf..000000000 --- a/app/models/foreman_openscap/oval_content.rb +++ /dev/null @@ -1,28 +0,0 @@ -module ForemanOpenscap - class OvalContent < ApplicationRecord - audited :except => [:scap_file] - include Authorizable - include Taxonomix - include ScapFileContent - - before_destroy EnsureNotUsedBy.new(:oval_policies) - - scoped_search :on => :name, :complete_value => true - - has_many :oval_policies - validates :name, :presence => true, :length => { :maximum => 255 }, uniqueness: true - validates :url, :format => { :with => %r{\Ahttps?://} }, :allow_blank => true - - before_validation :fetch_remote_content, :if => lambda { |oval_content| oval_content.url.present? } - - def to_h - { :id => id, :name => name, :original_filename => original_filename, :changed_at => changed_at } - end - - private - - def fetch_remote_content - ForemanOpenscap::Oval::SyncOvalContents.new.sync self - end - end -end diff --git a/app/models/foreman_openscap/oval_facet_oval_policy.rb b/app/models/foreman_openscap/oval_facet_oval_policy.rb deleted file mode 100644 index 5c6207463..000000000 --- a/app/models/foreman_openscap/oval_facet_oval_policy.rb +++ /dev/null @@ -1,6 +0,0 @@ -module ForemanOpenscap - class OvalFacetOvalPolicy < ApplicationRecord - belongs_to :oval_policy - belongs_to :oval_facet, :class_name => 'ForemanOpenscap::Host::OvalFacet' - end -end diff --git a/app/models/foreman_openscap/oval_policy.rb b/app/models/foreman_openscap/oval_policy.rb deleted file mode 100644 index be9881ffa..000000000 --- a/app/models/foreman_openscap/oval_policy.rb +++ /dev/null @@ -1,54 +0,0 @@ -module ForemanOpenscap - class OvalPolicy < ApplicationRecord - graphql_type '::Types::OvalPolicy' - - audited - include Authorizable - include Taxonomix - - include PolicyCommon - - belongs_to :oval_content - - validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 255 } - validates :period, :inclusion => { :in => %w[weekly monthly custom], :message => _('is not a valid value') } - validate :valid_cron_line, :valid_weekday, :valid_day_of_month - validates :oval_content, :presence => true - - has_many :oval_facet_oval_policies, :class_name => 'ForemanOpenscap::OvalFacetOvalPolicy' - has_many :oval_facets, :through => :oval_facet_oval_policies, :class_name => 'ForemanOpenscap::Host::OvalFacet' - has_many :hosts, :through => :oval_facets - - has_many :hostgroup_oval_facet_oval_policies, :class_name => 'ForemanOpenscap::HostgroupOvalFacetOvalPolicy' - has_many :hostgroup_oval_facets, :through => :hostgroup_oval_facet_oval_policies, :class_name => 'ForemanOpenscap::Hostgroup::OvalFacet', :source => :oval_facet - has_many :hostgroups, :through => :hostgroup_oval_facets - - has_many :host_cves - has_many :cves, :through => :host_cves - - def host_ids=(host_ids) - self.oval_facets = facets_to_assign(host_ids, :host_id, ForemanOpenscap::Host::OvalFacet) - end - - def hostgroup_ids=(hostgroup_ids) - self.hostgroup_oval_facets = facets_to_assign(hostgroup_ids, :hostgroup_id, ForemanOpenscap::Hostgroup::OvalFacet) - end - - def to_enc - { - :id => id, - :oval_content_path => "/var/lib/openscap/oval_content/#{oval_content.digest}.oval.xml.bz2", - :download_path => "/compliance/oval_policies/#{id}/oval_content/#{oval_content.digest}" - }.merge(period_enc).with_indifferent_access - end - - private - - def facets_to_assign(ids, key, facet_class) - filtered_ids = ids.uniq.reject { |id| respond_to?(:empty) && id.empty? } - existing_facets = facet_class.where(key => filtered_ids) - new_facets = (filtered_ids - existing_facets.pluck(key)).map { |id| facet_class.new(key => id) } - existing_facets + new_facets - end - end -end diff --git a/app/models/foreman_openscap/oval_status.rb b/app/models/foreman_openscap/oval_status.rb deleted file mode 100644 index 9ed6a80e3..000000000 --- a/app/models/foreman_openscap/oval_status.rb +++ /dev/null @@ -1,45 +0,0 @@ -module ForemanOpenscap - class OvalStatus < ::HostStatus::Status - PATCHED = 0 - VULNERABLE = 1 - PATCH_AVAILABLE = 2 - - def self.status_name - N_('OVAL scan') - end - - def to_label(options = {}) - case to_status - when PATCHED - N_('No Vulnerabilities found') - when VULNERABLE - N_("%s vulnerabilities found") % host.cves_without_errata.count - when PATCH_AVAILABLE - N_("%s vulnerabilities with available patch found") % host.cves_with_errata.count - else - N_('Unknown OVAL status') - end - end - - def to_global(options = {}) - case to_status - when PATCHED - ::HostStatus::Global::OK - when VULNERABLE - ::HostStatus::Global::WARN - when PATCH_AVAILABLE - ::HostStatus::Global::ERROR - end - end - - def relevant?(options = {}) - host.combined_oval_policies.any? - end - - def to_status(options = {}) - return PATCH_AVAILABLE if host.cves_with_errata.any? - return VULNERABLE if host.cves_without_errata.any? - PATCHED - end - end -end diff --git a/app/services/foreman_openscap/client_config/ansible.rb b/app/services/foreman_openscap/client_config/ansible.rb index 09106d6a6..b37ec3d59 100644 --- a/app/services/foreman_openscap/client_config/ansible.rb +++ b/app/services/foreman_openscap/client_config/ansible.rb @@ -37,7 +37,7 @@ def ansible_role_missing_msg private def policy_types - [ForemanOpenscap::Policy, ForemanOpenscap::OvalPolicy] + [ForemanOpenscap::Policy] end def initialize_constants(policy_class) @@ -59,15 +59,6 @@ def initialize_constants(policy_class) ) ) end - - if policy_class == ::ForemanOpenscap::OvalPolicy - @constants = OpenStruct.new( - base_constants.merge( - :policies_param => 'foreman_scap_client_oval_policies', - :policies_param_default_value => '<%= @host.oval_policies_enc %>' - ) - ) - end end end end diff --git a/app/services/foreman_openscap/oval/check_collection.rb b/app/services/foreman_openscap/oval/check_collection.rb deleted file mode 100644 index 45128a281..000000000 --- a/app/services/foreman_openscap/oval/check_collection.rb +++ /dev/null @@ -1,45 +0,0 @@ -module ForemanOpenscap - module Oval - class CheckCollection - attr_reader :checks - - def initialize(initial_check_attrs = []) - @checks = initial_check_attrs.map { |hash| SetupCheck.new hash } - end - - def all_passed? - @checks.all?(&:passed?) - end - - def find_check(check_id) - @checks.find { |item| item.id == check_id } - end - - def find_failed - @checks.select(&:failed?) - end - - def fail_check(check_id, error_data = nil) - find_check(check_id).fail_with! error_data - end - - def pass_check(check_id) - find_check(check_id).pass! - end - - def add_check(check) - @checks << check - self - end - - def merge(other) - @checks = @checks.concat other.checks - self - end - - def to_h - @checks.map(&:to_h) - end - end - end -end diff --git a/app/services/foreman_openscap/oval/configure.rb b/app/services/foreman_openscap/oval/configure.rb deleted file mode 100644 index 46e8e53aa..000000000 --- a/app/services/foreman_openscap/oval/configure.rb +++ /dev/null @@ -1,83 +0,0 @@ -module ForemanOpenscap - module Oval - class Configure - include ::ForemanOpenscap::HostgroupOverriderCommon - - def initialize - @config = ForemanOpenscap::ClientConfig::Ansible.new(::ForemanOpenscap::OvalPolicy) - end - - def assign(oval_policy, ids, model_class) - check_collection = ::ForemanOpenscap::Oval::Setup.new.run - return check_collection unless check_collection.all_passed? - - ansible_role = @config.find_config_item - - if model_class == ::Hostgroup - roles_method = :inherited_and_own_ansible_roles - ids_setter = :hostgroup_ids= - check_id = :hostgroups_without_proxy - elsif model_class == ::Host::Managed - roles_method = :all_ansible_roles - ids_setter = :host_ids= - check_id = :hosts_without_proxy - else - raise "Unexpected model_class, expected ::Hostgroup or ::Host::Managed, got: #{model_class}" - end - - items_with_proxy, items_without_proxy = openscap_proxy_associated(ids, model_class) - - - if items_without_proxy.any? - return without_proxy_to_check items_without_proxy, check_id - end - - oval_policy.send(ids_setter, items_with_proxy.pluck(:id)) - - unless oval_policy.save - return check_collection.add_check model_to_check(oval_policy, :oval_policy_errors) - end - - check_collection.merge modify_items(items_with_proxy, oval_policy, ansible_role, roles_method) - end - - private - - def openscap_proxy_associated(ids, model_class) - model_class.where(:id => ids).partition(&:openscap_proxy) - end - - def modify_items(items, oval_policy, ansible_role, roles_method) - items.reduce(CheckCollection.new) do |memo, item| - role_ids = item.ansible_role_ids + [ansible_role.id] - item.ansible_role_ids = role_ids unless item.send(roles_method).include? ansible_role - item.save if item.changed? - memo.add_check model_to_check(item, item.is_a?(::Hostgroup) ? 'hostgroup' : 'host') - add_overrides ansible_role.ansible_variables, item, @config - memo - end - end - - def without_proxy_to_check(items, check_id) - items.reduce(CheckCollection.new) do |memo, item| - memo.add_check( - SetupCheck.new( - :title => (_("Was %s configured successfully?") % item.class.name), - :fail_msg => (_("Assign openscap_proxy to %s before proceeding.") % item.name), - :id => check_id - ).fail! - ) - end - end - - def model_to_check(model, check_id) - check = SetupCheck.new( - :title => (_("Was %{model_name} %{name} configured successfully?") % { :model_name => model.class.name, :name => model.name }), - :errors => model.errors.to_h, - :id => check_id - ) - model.errors.any? ? check.fail! : check.pass! - end - end - end -end diff --git a/app/services/foreman_openscap/oval/cves.rb b/app/services/foreman_openscap/oval/cves.rb deleted file mode 100644 index 81a5104c7..000000000 --- a/app/services/foreman_openscap/oval/cves.rb +++ /dev/null @@ -1,41 +0,0 @@ -module ForemanOpenscap - module Oval - class Cves - def create(host, cve_data) - policy_id = cve_data['oval_policy_id'] - - incoming_cves = cve_data['oval_results'].reduce([]) do |memo, data| - next memo unless data['result'] == 'true' - cves, errata = data['references'].partition { |ref| ref['ref_id'].start_with?('CVE') } - - cves.map do |cve| - memo << ::ForemanOpenscap::Cve.find_or_create_by( - :ref_id => cve['ref_id'], - :ref_url => cve['ref_url'], - :has_errata => !errata.empty?, - :definition_id => data['definition_id'] - ) - end - memo - end - - current = ForemanOpenscap::Cve.of_oval_policy(policy_id).of_host(host.id) - to_delete = current - incoming_cves - to_create = incoming_cves - current - - ::ForemanOpenscap::HostCve.where(:host_id => host.id, :oval_policy_id => policy_id, :cve_id => to_delete.pluck(:id)).destroy_all - host.host_cves.build(to_create.map { |cve| { :host_id => host.id, :oval_policy_id => policy_id, :cve_id => cve.id } }) - - delete_orphaned_cves to_delete.pluck(:id) if host.save - host - end - - private - - def delete_orphaned_cves(ids) - associated_ids = ::ForemanOpenscap::HostCve.where(:cve_id => ids).select(:cve_id).distinct.pluck(:cve_id) - ::ForemanOpenscap::Cve.where(:id => ids - associated_ids).destroy_all - end - end - end -end diff --git a/app/services/foreman_openscap/oval/setup.rb b/app/services/foreman_openscap/oval/setup.rb deleted file mode 100644 index 5a94f9054..000000000 --- a/app/services/foreman_openscap/oval/setup.rb +++ /dev/null @@ -1,93 +0,0 @@ -module ForemanOpenscap - module Oval - class Setup - include ::ForemanOpenscap::LookupKeyOverridesCommon - - def initialize - @config = ForemanOpenscap::ClientConfig::Ansible.new(::ForemanOpenscap::OvalPolicy) - @check_collection = CheckCollection.new initial_check_attrs - end - - def run - override @config - @check_collection - end - - def handle_config_not_available(config) - return @check_collection.pass_check :foreman_ansible_present if config.available? - fail_check :foreman_ansible_present - end - - def handle_config_item_not_available(config, item) - return @check_collection.pass_check :foreman_scap_client_role_present if item - fail_check :foreman_scap_client_role_present - end - - def handle_missing_lookup_keys(config, key_names) - return @check_collection.pass_check :foreman_scap_client_vars_present if key_names.empty? - fail_check :foreman_scap_client_vars_present, :missing_vars => key_names - end - - def handle_server_param_override(config, param) - handle_param_override :foreman_scap_client_server_overriden, config, param - end - - def handle_port_param_override(config, param) - handle_param_override :foreman_scap_client_port_overriden, config, param - end - - def handle_policies_param_override(config, param) - handle_param_override :foreman_scap_client_policies_overriden, config, param - end - - def handle_param_override(check_id, config, param) - return fail_check check_id if param.changed? && !param.save - @check_collection.pass_check check_id - end - - def fail_check(check_id, error_data = nil) - @check_collection.fail_check(check_id, error_data) - false - end - - private - - def initial_check_attrs - override_msg = _("Could not update Ansible Variables with override: true") - - [ - { - :id => :foreman_ansible_present, - :title => _("Is foreman_ansible present?"), - :fail_msg => _("foreman_ansible plugin not found, please install it before running this action again.") - }, - { - :id => :foreman_scap_client_role_present, - :title => _("Is theforeman.foreman_scap_client present?"), - :fail_msg => @config.ansible_role_missing_msg - }, - { - :id => :foreman_scap_client_vars_present, - :title => _("Are required variables for theforeman.foreman_scap_client present?"), - :fail_msg => ->(hash) { _("The following Ansible Variables were not found: %{missing_vars}, please import them before running this action again.") % hash } - }, - { - :id => :foreman_scap_client_server_overriden, - :title => _("Is %s param set to be overriden?") % @config.server_param, - :fail_msg => override_msg - }, - { - :id => :foreman_scap_client_port_overriden, - :title => _("Is %s param set to be overriden?") % @config.port_param, - :fail_msg => override_msg - }, - { - :id => :foreman_scap_client_policies_overriden, - :title => _("Is %s param set to be overriden?") % @config.policies_param, - :fail_msg => override_msg - } - ] - end - end - end -end diff --git a/app/services/foreman_openscap/oval/setup_check.rb b/app/services/foreman_openscap/oval/setup_check.rb deleted file mode 100644 index f37ba8264..000000000 --- a/app/services/foreman_openscap/oval/setup_check.rb +++ /dev/null @@ -1,58 +0,0 @@ -module ForemanOpenscap - module Oval - class SetupCheck - attr_reader :result, :id, :title, :errors - - def initialize(hash) - @id = hash[:id] - @title = hash[:title] - @fail_msg = hash[:fail_msg] - @errors = hash[:errors] - @result = :skip - end - - def fail_with!(fail_data) - @fail_msg_data = fail_data - fail! - end - - def fail! - raise 'Cannot fail a check that expects fail message data, use fail_with! method instead' if @fail_msg.respond_to?(:call) && @fail_msg_data.empty? - @result = :fail - self - end - - def pass! - @result = :pass - self - end - - def failed? - @result == :fail - end - - def passed? - @result == :pass - end - - def skipped? - @result == :skip - end - - def fail_msg - return unless failed? - return @fail_msg.call(@fail_msg_data) if @fail_msg.respond_to?(:call) && @fail_msg_data - @fail_msg - end - - def to_h - { - :title => @title, - :result => @result, - :fail_message => failed? ? fail_msg : nil, - :errors => @errors - } - end - end - end -end diff --git a/app/services/foreman_openscap/oval/sync_oval_contents.rb b/app/services/foreman_openscap/oval/sync_oval_contents.rb deleted file mode 100644 index ec7cf35cf..000000000 --- a/app/services/foreman_openscap/oval/sync_oval_contents.rb +++ /dev/null @@ -1,42 +0,0 @@ -module ForemanOpenscap - module Oval - class SyncOvalContents - def sync(oval_content) - begin - content_blob = fetch_content_blob(oval_content.url) - rescue StandardError => e - oval_content.errors.add(:base, "#{fail_msg oval_content}, " + _("cause: ") + e.message) - return oval_content - end - - unless content_blob - oval_content.errors.add(:base, fail_msg(oval_content)) - return oval_content - end - oval_content.scap_file = content_blob - oval_content - end - - def sync_all - to_sync = ForemanOpenscap::OvalContent.where.not(:url => nil) - to_sync.map { |content| content.tap { |item| sync(item).save } } - end - - private - - def fail_msg(content) - _("Failed to fetch content file from %s") % content.url - end - - def fetch_content_blob(url) - response = fetch url - return unless response.code == 200 - response.body - end - - def fetch(url) - RestClient.get(url) - end - end - end -end diff --git a/app/views/api/v2/compliance/oval_contents/base.json.rabl b/app/views/api/v2/compliance/oval_contents/base.json.rabl deleted file mode 100644 index f289c95d5..000000000 --- a/app/views/api/v2/compliance/oval_contents/base.json.rabl +++ /dev/null @@ -1,6 +0,0 @@ -object @oval_content - -extends "api/v2/compliance/common/org" -extends "api/v2/compliance/common/loc" - -attributes :id, :name, :original_filename, :digest, :created_at, :updated_at, :url diff --git a/app/views/api/v2/compliance/oval_contents/create.json.rabl b/app/views/api/v2/compliance/oval_contents/create.json.rabl deleted file mode 100644 index d7fa79416..000000000 --- a/app/views/api/v2/compliance/oval_contents/create.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_content - -extends "api/v2/compliance/oval_contents/base" diff --git a/app/views/api/v2/compliance/oval_contents/destroy.json.rabl b/app/views/api/v2/compliance/oval_contents/destroy.json.rabl deleted file mode 100644 index d7fa79416..000000000 --- a/app/views/api/v2/compliance/oval_contents/destroy.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_content - -extends "api/v2/compliance/oval_contents/base" diff --git a/app/views/api/v2/compliance/oval_contents/index.json.rabl b/app/views/api/v2/compliance/oval_contents/index.json.rabl deleted file mode 100644 index 738ae4834..000000000 --- a/app/views/api/v2/compliance/oval_contents/index.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -collection @oval_contents - -extends "api/v2/compliance/oval_contents/base" diff --git a/app/views/api/v2/compliance/oval_contents/show.json.rabl b/app/views/api/v2/compliance/oval_contents/show.json.rabl deleted file mode 100644 index d7fa79416..000000000 --- a/app/views/api/v2/compliance/oval_contents/show.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_content - -extends "api/v2/compliance/oval_contents/base" diff --git a/app/views/api/v2/compliance/oval_contents/sync.json.rabl b/app/views/api/v2/compliance/oval_contents/sync.json.rabl deleted file mode 100644 index 5893f07ef..000000000 --- a/app/views/api/v2/compliance/oval_contents/sync.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -collection @oval_contents - -extends "api/v2/compliance/oval_contents/sync_result" diff --git a/app/views/api/v2/compliance/oval_contents/sync_result.json.rabl b/app/views/api/v2/compliance/oval_contents/sync_result.json.rabl deleted file mode 100644 index 6a6012c47..000000000 --- a/app/views/api/v2/compliance/oval_contents/sync_result.json.rabl +++ /dev/null @@ -1,11 +0,0 @@ -object @oval_content - -attributes :id, :name - -node(:errors) do |content| - content.errors.to_hash -end - -node(:full_messages) do |content| - content.errors.full_messages -end diff --git a/app/views/api/v2/compliance/oval_contents/update.json.rabl b/app/views/api/v2/compliance/oval_contents/update.json.rabl deleted file mode 100644 index d7fa79416..000000000 --- a/app/views/api/v2/compliance/oval_contents/update.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_content - -extends "api/v2/compliance/oval_contents/base" diff --git a/app/views/api/v2/compliance/oval_policies/create.json.rabl b/app/views/api/v2/compliance/oval_policies/create.json.rabl deleted file mode 100644 index 558a96621..000000000 --- a/app/views/api/v2/compliance/oval_policies/create.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_policy - -extends "api/v2/compliance/oval_policies/main" diff --git a/app/views/api/v2/compliance/oval_policies/index.json.rabl b/app/views/api/v2/compliance/oval_policies/index.json.rabl deleted file mode 100644 index 6cc0015ca..000000000 --- a/app/views/api/v2/compliance/oval_policies/index.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -collection @oval_policies - -extends "api/v2/compliance/oval_policies/main" diff --git a/app/views/api/v2/compliance/oval_policies/main.json.rabl b/app/views/api/v2/compliance/oval_policies/main.json.rabl deleted file mode 100644 index 584cc6856..000000000 --- a/app/views/api/v2/compliance/oval_policies/main.json.rabl +++ /dev/null @@ -1,15 +0,0 @@ -object @oval_policy - -extends "api/v2/compliance/common/org" -extends "api/v2/compliance/common/loc" -extends "api/v2/compliance/policies_common/attrs" - -attributes :created_at, :updated_at, :oval_content_id - -child :hosts => :hosts do |host| - attributes :id, :name -end - -child :hostgroups => :hostgroups do |hg| - attributes :id, :name -end diff --git a/app/views/api/v2/compliance/oval_policies/show.json.rabl b/app/views/api/v2/compliance/oval_policies/show.json.rabl deleted file mode 100644 index 558a96621..000000000 --- a/app/views/api/v2/compliance/oval_policies/show.json.rabl +++ /dev/null @@ -1,3 +0,0 @@ -object @oval_policy - -extends "api/v2/compliance/oval_policies/main" diff --git a/app/views/job_templates/run_oval_scans.erb b/app/views/job_templates/run_oval_scans.erb deleted file mode 100644 index d392352fb..000000000 --- a/app/views/job_templates/run_oval_scans.erb +++ /dev/null @@ -1,24 +0,0 @@ -<%# -name: Run OVAL scans -job_category: OpenSCAP -description_format: Run scan for specified OVAL Policies -feature: foreman_openscap_run_oval_scans -provider_type: SSH -snippet: false -provider_type: SSH -kind: job_template -template_inputs: -- name: oval_policies - description: Comma separated OVAL Policy Ids to run - input_type: user - advanced: true --%> -<% unless input('oval_policies').blank? -%> - <% input('oval_policies').split(',').map do |id| -%> - /usr/bin/foreman_scap_client oval <%= id %> - <% end -%> -<% else -%> - <% @host.oval_policies_enc_raw.map do |policy| -%> - /usr/bin/foreman_scap_client oval <%= policy['id'] %> - <% end -%> -<% end -%> diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index a37c9f9e6..c5631ec9d 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -7,6 +7,4 @@ # inflect.singular /^(ox)en/i, '\1' # inflect.irregular 'person', 'people' # inflect.uncountable %w(fish sheep) - - inflect.singular 'cves', 'cve' end diff --git a/config/routes.rb b/config/routes.rb index 3ac179be3..ee32102ab 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -94,21 +94,6 @@ post 'arf_reports/:cname/:policy_id/:date', \ :constraints => { :cname => /[^\/]+/ }, :to => 'arf_reports#create' - - resources :oval_contents, :except => %i[new edit] do - collection do - post 'sync' - end - end - - resources :oval_policies, :except => %i[new edit] do - member do - post 'assign_hostgroups' - post 'assign_hosts' - get 'oval_content' - end - end - post 'oval_reports/:cname/:oval_policy_id/:date', :constraints => { :cname => /[^\/]+/ }, :to => 'oval_reports#create' end end end diff --git a/db/migrate/20240313111822_drop_oval.rb b/db/migrate/20240313111822_drop_oval.rb new file mode 100644 index 000000000..e20c72e48 --- /dev/null +++ b/db/migrate/20240313111822_drop_oval.rb @@ -0,0 +1,17 @@ +class DropOval < ActiveRecord::Migration[6.1] + def up + drop_table :foreman_openscap_host_cves + drop_table :foreman_openscap_oval_contents + drop_table :foreman_openscap_oval_policies + drop_table :foreman_openscap_hostgroup_oval_facet_oval_policies + drop_table :foreman_openscap_hostgroup_oval_facets + drop_table :foreman_openscap_oval_facet_oval_policies + drop_table :foreman_openscap_oval_facets + drop_table :foreman_openscap_cves + + scope = ::HostStatus::Status.where(type: 'ForemanOpenscap::OvalStatus') + host_ids = scope.pluck(:host_id) + scope.delete_all + ::Host::Managed.where(id: host_ids).find_each(&:refresh_global_status!) + end +end diff --git a/lib/foreman_openscap/engine.rb b/lib/foreman_openscap/engine.rb index 2229c45aa..733dbc275 100644 --- a/lib/foreman_openscap/engine.rb +++ b/lib/foreman_openscap/engine.rb @@ -43,7 +43,7 @@ class Engine < ::Rails::Engine end initializer 'foreman_openscap.filter_large_params' do |app| - app.config.filter_parameters += %i[logs scap_file oval_results] if app.config.filter_parameters + app.config.filter_parameters += %i[logs scap_file] if app.config.filter_parameters end initializer 'foreman_openscap.register_plugin', :before => :finisher_hook do |app| @@ -54,7 +54,6 @@ class Engine < ::Rails::Engine apipie_documented_controllers ["#{ForemanOpenscap::Engine.root}/app/controllers/api/v2/compliance/*.rb"] register_custom_status ForemanOpenscap::ComplianceStatus - register_custom_status ForemanOpenscap::OvalStatus # Add permissions security_block :foreman_openscap do @@ -123,24 +122,6 @@ class Engine < ::Rails::Engine :resource_type => 'ForemanOpenscap::TailoringFile' permission :view_openscap_proxies, { :openscap_proxies => [:openscap_spool] }, :resource_type => 'SmartProxy' - permission :view_oval_contents, { 'api/v2/compliance/oval_contents' => %i[index show] }, - :resource_type => 'ForemanOpenscap::OvalContent' - permission :edit_oval_contents, { 'api/v2/compliance/oval_contents' => %i[update sync] }, - :resource_type => 'ForemanOpenscap::OvalContent' - permission :create_oval_contents, { 'api/v2/compliance/oval_contents' => %i[create] }, - :resource_type => 'ForemanOpenscap::OvalContent' - permission :destroy_oval_contents, { 'api/v2/compliance/oval_contents' => %i[destroy] }, - :resource_type => 'ForemanOpenscap::OvalContent' - permission :view_oval_policies, { 'api/v2/compliance/oval_policies' => %i[index show oval_content] }, - :resource_type => 'ForemanOpenscap::OvalPolicy' - permission :edit_oval_policies, { 'api/v2/compliance/oval_policies' => %i[update assign_hosts assign_hostgroups] }, - :resource_type => 'ForemanOpenscap::OvalPolicy' - permission :create_oval_policies, { 'api/v2/compliance/oval_policies' => %i[create] }, - :resource_type => 'ForemanOpenscap::OvalPolicy' - permission :destroy_oval_policies, { 'api/v2/compliance/oval_policies' => %i[destroy] }, - :resource_type => 'ForemanOpenscap::OvalPolicy' - permission :create_oval_policies, { 'api/v2/compliance/oval_reports' => %i[create] }, - :resource_type => 'ForemanOpenscap::Cve' end role "Compliance viewer", %i[view_arf_reports view_policies view_scap_contents view_tailoring_files view_openscap_proxies], @@ -169,15 +150,7 @@ class Engine < ::Rails::Engine menu :top_menu, :compliance_files, :caption => N_('Tailoring Files'), :url_hash => { :controller => :tailoring_files, :action => :index }, :parent => :hosts_menu - menu :labs_menu, :oval_contents, :caption => N_('OVAL Contents'), - :url_hash => { :controller => 'react', :action => 'index' }, - :url => '/experimental/compliance/oval_contents', - :parent => :lab_features_menu - - menu :labs_menu, :oval_policies, :caption => N_('OVAL Policies'), - :url_hash => { :controller => 'react', :action => 'index' }, - :url => '/experimental/compliance/oval_policies', - :parent => :lab_features_menu + # add dashboard widget widget 'compliance_host_reports_widget', :name => N_('Latest Compliance Reports'), :sizex => 6, :sizey => 1 @@ -219,27 +192,6 @@ class Engine < ::Rails::Engine register_global_js_file 'global' - register_graphql_query_field :oval_contents, '::Types::OvalContent', :collection_field - register_graphql_query_field :oval_content, '::Types::OvalContent', :record_field - register_graphql_query_field :oval_policies, '::Types::OvalPolicy', :collection_field - register_graphql_query_field :oval_policy, '::Types::OvalPolicy', :record_field - register_graphql_query_field :cves, '::Types::Cve', :collection_field - - register_graphql_mutation_field :delete_oval_policy, ::Mutations::OvalPolicies::Delete - register_graphql_mutation_field :delete_oval_content, ::Mutations::OvalContents::Delete - register_graphql_mutation_field :update_oval_policy, ::Mutations::OvalPolicies::Update - register_graphql_mutation_field :create_oval_policy, ::Mutations::OvalPolicies::Create - - register_facet ForemanOpenscap::Host::OvalFacet, :oval_facet do - configure_host do - extend_model ForemanOpenscap::OvalFacetHostExtensions - end - - configure_hostgroup(ForemanOpenscap::Hostgroup::OvalFacet) do - extend_model ForemanOpenscap::OvalFacetHostgroupExtensions - end - end - describe_host do multiple_actions_provider :compliance_host_multiple_actions overview_buttons_provider :compliance_host_overview_button @@ -271,27 +223,11 @@ class Engine < ::Rails::Engine :provided_inputs => "policies" } - oval_options = { - :description => N_("Run OVAL scan") - } - - ansible_remediation_options = { - :description => N_("Run OpenSCAP remediation with Ansible"), - :provided_inputs => ["tasks", "reboot"] - } - - script_remediation_options = { - :description => N_("Run OpenSCAP remediation with Shell"), - :provided_inputs => ["command", "reboot"] - } - if Gem::Version.new(ForemanRemoteExecution::VERSION) >= Gem::Version.new('1.2.3') options[:host_action_button] = true - oval_options[:host_action_button] = (!::Foreman.in_rake? && ActiveRecord::Base.connection.table_exists?(:settings)) ? (Setting.find_by(:name => 'lab_features')&.value || false) : false end RemoteExecutionFeature.register(:foreman_openscap_run_scans, N_("Run OpenSCAP scan"), options) - RemoteExecutionFeature.register(:foreman_openscap_run_oval_scans, N_("Run OVAL scan"), oval_options) RemoteExecutionFeature.register(:ansible_run_openscap_remediation, N_("Run OpenSCAP remediation with Ansible"), ansible_remediation_options) RemoteExecutionFeature.register(:script_run_openscap_remediation, N_("Run OpenSCAP remediation with Shell"), script_remediation_options) end diff --git a/test/factories/compliance_host_factory.rb b/test/factories/compliance_host_factory.rb index 12dea320d..df4092568 100644 --- a/test/factories/compliance_host_factory.rb +++ b/test/factories/compliance_host_factory.rb @@ -16,16 +16,4 @@ openscap_proxy { SmartProxy.unscoped.with_features('Openscap').first || FactoryBot.create(:openscap_proxy) } policies { [] } end - - factory :oval_facet, :class => ForemanOpenscap::Host::OvalFacet - - factory :oval_host, :class => Host::Managed do - sequence(:name) { |n| "host#{n}" } - end - - factory :cve, :class => ForemanOpenscap::Cve do - sequence(:ref_id) { |n| "CVE-#{n}" } - sequence(:ref_url) { |n| "https://access.redhat.com/security/cve/CVE-#{n}" } - sequence(:definition_id) { |n| "oval:com.redhat.rhsa:def:202015#{n}" } - end end diff --git a/test/factories/oval_content_factory.rb b/test/factories/oval_content_factory.rb deleted file mode 100644 index b9ff74452..000000000 --- a/test/factories/oval_content_factory.rb +++ /dev/null @@ -1,7 +0,0 @@ -FactoryBot.define do - factory :oval_content, :class => ::ForemanOpenscap::OvalContent do |f| - f.sequence(:name) { |n| "oval_content_#{n}" } - f.original_filename { 'test-oval.xml' } - f.scap_file { 'foo' } - end -end diff --git a/test/factories/oval_policy_factory.rb b/test/factories/oval_policy_factory.rb deleted file mode 100644 index b15614adb..000000000 --- a/test/factories/oval_policy_factory.rb +++ /dev/null @@ -1,9 +0,0 @@ -FactoryBot.define do - factory :oval_policy, :class => ::ForemanOpenscap::OvalPolicy do - sequence(:name) { |n| "policy#{n}" } - period { 'weekly' } - weekday { 'monday' } - day_of_month { nil } - cron_line { nil } - end -end diff --git a/test/fixtures/cve_fixtures.rb b/test/fixtures/cve_fixtures.rb deleted file mode 100644 index 403d0b922..000000000 --- a/test/fixtures/cve_fixtures.rb +++ /dev/null @@ -1,104 +0,0 @@ -module ForemanOpenscap - class CveFixtures - def res_one(result_state = 'true') - init_result( - { "references" => [ - { "ref_id" => "RHSA-2020:0215", "ref_url" => "https://access.redhat.com/errata/RHSA-2020:0215" }, - { "ref_id" => "CVE-2019-16541", "ref_url" => "https://access.redhat.com/security/cve/CVE-2019-16541" }, - { "ref_id" => "CVE-2020-14040", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-14040" }, - { "ref_id" => "CVE-2020-14370", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-14370" }, - { "ref_id" => "CVE-2020-15586", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-15586" }, - { "ref_id" => "CVE-2020-16845", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-16845" }, - { "ref_id" => "CVE-2020-2252", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2252" }, - { "ref_id" => "CVE-2020-2254", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2254" }, - { "ref_id" => "CVE-2020-2255", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2255" }, - { "ref_id" => "CVE-2020-8564", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-8564" } - ] }, - result_state, - "oval:com.redhat.rhsa:def:20201545" - ) - end - - def res_two(result_state = 'true') - init_result( - { "references" => [ - { "ref_id" => "RHSA-2020:3601", "ref_url" => "https://access.redhat.com/errata/RHSA-2020:3601" }, - { "ref_id" => "CVE-2020-2181", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2181" }, - { "ref_id" => "CVE-2020-2182", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2182" }, - { "ref_id" => "CVE-2020-2224", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2224" }, - { "ref_id" => "CVE-2020-2225", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2225" }, - { "ref_id" => "CVE-2020-2226", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2226" } - ] }, - result_state, - "oval:com.redhat.rhsa:def:20201544" - ) - end - - def res_three(result_state = 'true') - init_result( - { "references" => [ - { "ref_id" => "CVE-2019-17638", "ref_url" => "https://access.redhat.com/security/cve/CVE-2019-17638" }, - { "ref_id" => "CVE-2020-2229", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2229" }, - { "ref_id" => "CVE-2020-2230", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2230" }, - { "ref_id" => "CVE-2020-2231", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2231" } - ] }, - result_state, - "oval:com.redhat.rhsa:def:20201543" - ) - end - - def res_four(result_state = 'true') - init_result( - { "references" => [ - { "ref_id" => "RHSA-2020:3601", "ref_url" => "https://access.redhat.com/errata/RHSA-2020:3601" }, - { "ref_id" => "CVE-2019-17638", "ref_url" => "https://access.redhat.com/security/cve/CVE-2019-17638" }, - { "ref_id" => "CVE-2020-2220", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2220" }, - { "ref_id" => "CVE-2020-2221", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2221" }, - { "ref_id" => "CVE-2020-2222", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2222" }, - { "ref_id" => "CVE-2020-2223", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2223" }, - { "ref_id" => "CVE-2020-2229", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2229" }, - { "ref_id" => "CVE-2020-2230", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2230" }, - { "ref_id" => "CVE-2020-2231", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2231" }, - { "ref_id" => "CVE-2020-8557", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-8557" } - ] }, - result_state, - "oval:com.redhat.rhsa:def:20201542" - ) - end - - def res_five(result_state = 'true') - init_result( - { "references" => [ - { "ref_id" => "CVE-2020-2181", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2181" }, - { "ref_id" => "CVE-2020-2182", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2182" }, - { "ref_id" => "CVE-2020-2190", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2190" }, - { "ref_id" => "CVE-2020-2224", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2224" }, - { "ref_id" => "CVE-2020-2225", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2225" }, - { "ref_id" => "CVE-2020-2226", "ref_url" => "https://access.redhat.com/security/cve/CVE-2020-2226" } - ] }, - result_state, - "oval:com.redhat.rhsa:def:20201541" - ) - end - - def one - [res_one, res_two, res_three, res_four, res_five] - end - - def two - [res_one('false'), res_two, res_three('false')] - end - - def ids_from(fixture) - fixture['references'].pluck('ref_id') - end - - private - - def init_result(data, result_state, definition_id) - data['result'] = result_state - data['definition_id'] = definition_id - data - end - end -end diff --git a/test/functional/api/v2/compliance/oval_contents_controller_test.rb b/test/functional/api/v2/compliance/oval_contents_controller_test.rb deleted file mode 100644 index c212f8a13..000000000 --- a/test/functional/api/v2/compliance/oval_contents_controller_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'test_plugin_helper' -require 'tempfile' - -class Api::V2::Compliance::OvalContentsControllerTest < ActionController::TestCase - test "should get index" do - FactoryBot.create(:oval_content) - get :index, :session => set_session_user - response = ActiveSupport::JSON.decode(@response.body) - assert response['results'].any? - assert_response :success - end - - test "should create OVAL content" do - post :create, :params => { :oval_content => { :name => 'OVAL test', :scap_file => content_file } }, :session => set_session_user - assert_response :success - end - - test "should update OVAL content" do - new_name = 'RHEL7 OVAL' - oval_content = FactoryBot.create(:oval_content) - put :update, :params => { :id => oval_content.id, :oval_content => { :name => new_name } }, :session => set_session_user - assert_response :success - assert oval_content.name, new_name - end - - test "should destory OVAL content" do - oval_content = FactoryBot.create(:oval_content) - delete :destroy, :params => { :id => oval_content.id }, :session => set_session_user - assert_response :ok - refute ForemanOpenscap::OvalContent.exists?(oval_content.id) - end - - def content_file - file = Tempfile.new('test') - file.write('test') - file.rewind - Rack::Test::UploadedFile.new(file, '') - end -end diff --git a/test/functional/api/v2/compliance/oval_policies_controller_test.rb b/test/functional/api/v2/compliance/oval_policies_controller_test.rb deleted file mode 100644 index 19209e664..000000000 --- a/test/functional/api/v2/compliance/oval_policies_controller_test.rb +++ /dev/null @@ -1,141 +0,0 @@ -require 'test_plugin_helper' -require 'base64' - -class Api::V2::Compliance::OvalPoliciesControllerTest < ActionController::TestCase - setup do - @file = Base64.encode64(read_oval_content('ansible-2.9.oval.xml.bz2')) - oval_content = FactoryBot.create(:oval_content, :scap_file => @file) - @attributes = { :oval_policy => { :name => 'my_policy', :period => 'weekly', :weekday => 'friday', :oval_content_id => oval_content.id } } - @config = ForemanOpenscap::ClientConfig::Ansible.new(::ForemanOpenscap::OvalPolicy) - @policy = FactoryBot.create(:oval_policy, :oval_content => oval_content) - end - - test "should get index of OVAL policies" do - get :index, :session => set_session_user - response = ActiveSupport::JSON.decode(@response.body) - assert !response['results'].empty? - assert_response :success - end - - test "should show OVAL policy" do - get :show, :params => { :id => @policy.to_param }, :session => set_session_user - response = ActiveSupport::JSON.decode(@response.body) - assert response['name'], @policy.name - assert_response :success - end - - test "should update OVAL policy" do - put :update, :params => { :id => @policy.id, :oval_policy => { :period => 'monthly', :day_of_month => 15 } } - updated_policy = ActiveSupport::JSON.decode(@response.body) - assert(updated_policy['period'], 'monthly') - assert_response :ok - end - - test "should not update invalid OVAL policy" do - put :update, :params => { :id => @policy.id, :oval_policy => { :name => '' } } - assert_response :unprocessable_entity - end - - test "should create OVAL policy" do - post :create, :params => @attributes, :session => set_session_user - assert_response :created - end - - test "should not create invalid OVAL policy" do - post :create, :session => set_session_user - assert_response :unprocessable_entity - end - - test "should destroy OVAL policy" do - delete :destroy, :params => { :id => @policy.id }, :session => set_session_user - assert_response :ok - refute ForemanOpenscap::OvalPolicy.exists?(@policy.id) - end - - test "should return error when OVAL policy not found" do - get :show, :params => { :id => @policy.id + 1 }, :session => set_session_user - response = ActiveSupport::JSON.decode(@response.body) - assert response['error'] - assert_response :missing - end - - test "should assign policy to multiple hosts correctly" do - proxy = FactoryBot.create(:openscap_proxy) - host1 = FactoryBot.create(:compliance_host, :openscap_proxy => proxy) - host2 = FactoryBot.create(:compliance_host, :openscap_proxy => proxy) - setup_ansible - - assert_empty host1.oval_policies - assert_empty host2.oval_policies - - post :assign_hosts, :params => { :id => @policy.id, :host_ids => [host1, host2].pluck(:id) }, :session => set_session_user - assert_equal "OVAL policy successfully configured with hosts.", ActiveSupport::JSON.decode(@response.body)['message'] - - assert_equal 2, host1.lookup_values.count - server_value = @server_key.lookup_values.find_by :match => "fqdn=#{host1.name}" - port_value = @port_key.lookup_values.find_by :match => "fqdn=#{host1.name}" - assert_equal proxy.hostname, server_value.value - assert_equal proxy.port, port_value.value - end - - test "should assign policy to multiple hostgroups correctly" do - proxy = FactoryBot.create(:openscap_proxy) - hg1 = FactoryBot.create(:hostgroup, :openscap_proxy => proxy) - hg2 = FactoryBot.create(:hostgroup, :openscap_proxy => proxy) - setup_ansible - - assert_empty hg1.oval_policies - assert_empty hg2.oval_policies - - post :assign_hostgroups, :params => { :id => @policy.id, :hostgroup_ids => [hg1, hg2].pluck(:id) }, :session => set_session_user - assert_equal "OVAL policy successfully configured with hostgroups.", ActiveSupport::JSON.decode(@response.body)['message'] - - assert_equal 2, hg1.lookup_values.count - server_value = @server_key.lookup_values.find_by :match => "hostgroup=#{hg1.name}" - port_value = @port_key.lookup_values.find_by :match => "hostgroup=#{hg1.name}" - assert_equal proxy.hostname, server_value.value - assert_equal proxy.port, port_value.value - end - - test "should not assign policy to hostgroup without openscap proxy" do - hg = FactoryBot.create(:hostgroup) - setup_ansible - - assert_empty hg.oval_policies - - post :assign_hostgroups, :params => { :id => @policy.id, :hostgroup_ids => hg.id }, :session => set_session_user - res = ActiveSupport::JSON.decode(@response.body)['results'].first - assert_equal "Was Hostgroup configured successfully?", res['title'] - assert_equal "fail", res['result'] - assert_equal "Assign openscap_proxy to #{hg.name} before proceeding.", res['fail_message'] - hg.reload - assert_empty hg.oval_policies - end - - test "should not assign policy to hostgroup when ansible role not present" do - hg = FactoryBot.create(:hostgroup) - assert_empty hg.oval_policies - - post :assign_hostgroups, :params => { :id => @policy.id, :hostgroup_ids => hg.id }, :session => set_session_user - res = ActiveSupport::JSON.decode(@response.body)['results'].first - assert_equal 'theforeman.foreman_scap_client Ansible Role not found, please import it before running this action again.', res['fail_message'] - hg.reload - assert_empty hg.oval_policies - end - - test "should show oval content" do - get :oval_content, :params => { :id => @policy.id } - assert response.body, @file - end - - def setup_ansible - @ansible_role = FactoryBot.create(:ansible_role, :name => @config.ansible_role_name) - @port_key = FactoryBot.create(:ansible_variable, :key => @config.port_param, :ansible_role => @ansible_role) - @server_key = FactoryBot.create(:ansible_variable, :key => @config.server_param, :ansible_role => @ansible_role) - FactoryBot.create(:ansible_variable, :key => @config.policies_param, :ansible_role => @ansible_role) - end - - def read_oval_content(file_name) - File.read "#{ForemanOpenscap::Engine.root}/test/files/oval_contents/#{file_name}" - end -end diff --git a/test/functional/api/v2/compliance/oval_reports_controller_test.rb b/test/functional/api/v2/compliance/oval_reports_controller_test.rb deleted file mode 100644 index def29310c..000000000 --- a/test/functional/api/v2/compliance/oval_reports_controller_test.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'test_plugin_helper' - -class Api::V2::Compliance::OvalReportsControllerTest < ActionController::TestCase - setup do - @params = { - :oval_results => ForemanOpenscap::CveFixtures.new.one, - :oval_policy_id => 5, - :date => Time.now.to_i - } - end - - test 'should accept new CVEs for host' do - host = FactoryBot.create(:host) - post :create, :params => @params.merge(:cname => host.name), :session => set_session_user - - response = ActiveSupport::JSON.decode(@response.body) - assert_equal 'ok', response['result'] - assert_response :success - end - - test 'should show host errors on CVEs upload' do - proxy = FactoryBot.create(:smart_proxy) - host = FactoryBot.create(:host, :puppet_proxy => proxy) - SmartProxy.any_instance.stubs(:smart_proxy_features).returns([]) - post :create, :params => @params.merge(:cname => host.name), :session => set_session_user - - response = ActiveSupport::JSON.decode(@response.body) - assert_equal 'fail', response['result'] - refute response['errors'].empty? - assert_response :unprocessable_entity - end -end diff --git a/test/graphql/mutations/oval_policies/delete_mutation_test.rb b/test/graphql/mutations/oval_policies/delete_mutation_test.rb deleted file mode 100644 index 1d93dc05e..000000000 --- a/test/graphql/mutations/oval_policies/delete_mutation_test.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'test_plugin_helper' - -module Mutations - module OvalPolicies - class DeleteMutationTest < ActiveSupport::TestCase - let(:policy) { FactoryBot.create(:oval_policy, :oval_content => FactoryBot.create(:oval_content)) } - let(:policy_id) { Foreman::GlobalId.for(policy) } - let(:variables) do - { - id: policy_id, - } - end - let(:query) do - <<-GRAPHQL - mutation DeleteOvalPolicyMutation($id:ID!){ - deleteOvalPolicy(input:{id:$id}) { - id - errors { - message - path - } - } - } - GRAPHQL - end - - context 'with admin user' do - let(:user) { FactoryBot.create(:user, :admin) } - - test 'should delete oval policy' do - context = { current_user: user } - - policy - - assert_difference('::ForemanOpenscap::OvalPolicy.count', -1) do - result = ForemanGraphqlSchema.execute(query, variables: variables, context: context) - assert_empty result['errors'] - assert_empty result['data']['deleteOvalPolicy']['errors'] - assert_equal policy_id, result['data']['deleteOvalPolicy']['id'] - end - assert_equal user.id, Audit.last.user_id - end - end - - context 'with user with view permissions' do - setup do - policy - @user = setup_user 'view', 'oval_policies' - end - - test 'should not delete oval policy' do - context = { current_user: @user } - - assert_difference('ForemanOpenscap::OvalPolicy.count', 0) do - result = ForemanGraphqlSchema.execute(query, variables: variables, context: context) - assert_not_empty result['errors'] - assert_includes result['errors'].map { |error| error['message'] }.to_sentence, 'Unauthorized.' - end - end - end - end - end -end diff --git a/test/graphql/queries/oval_content_query_test.rb b/test/graphql/queries/oval_content_query_test.rb deleted file mode 100644 index 1937c8e5c..000000000 --- a/test/graphql/queries/oval_content_query_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'test_plugin_helper' - -module Queries - class OvalContentQueryTest < GraphQLQueryTestCase - let(:query) do - <<-GRAPHQL - query($id:String!) { - ovalContent(id: $id) { - id - name - originalFilename - url - } - } - GRAPHQL - end - - let(:oval_content) { FactoryBot.create(:oval_content) } - - let(:global_id) { Foreman::GlobalId.for(oval_content) } - let(:variables) { { id: global_id } } - let(:data) { result['data']['ovalContent'] } - - test 'should return OVAL Content' do - assert_equal global_id, data['id'] - assert_equal oval_content.name, data['name'] - end - end -end diff --git a/test/graphql/queries/oval_contents_query_test.rb b/test/graphql/queries/oval_contents_query_test.rb deleted file mode 100644 index 192771377..000000000 --- a/test/graphql/queries/oval_contents_query_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'test_plugin_helper' - -module Queries - class OvalContentsQueryTest < GraphQLQueryTestCase - let(:query) do - <<-GRAPHQL - query { - ovalContents { - totalCount - nodes { - id - name - } - } - } - GRAPHQL - end - - let(:data) { result['data']['ovalContents'] } - - setup do - FactoryBot.create_list(:oval_content, 2) - end - - test 'should fetch oval contentes' do - assert_empty result['errors'] - - expected_count = ForemanOpenscap::OvalContent.count - - assert_not_equal 0, expected_count - assert_equal expected_count, data['totalCount'] - assert_equal expected_count, data['nodes'].count - end - end -end diff --git a/test/graphql/queries/oval_policies_query_test.rb b/test/graphql/queries/oval_policies_query_test.rb deleted file mode 100644 index db8df79a8..000000000 --- a/test/graphql/queries/oval_policies_query_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'test_plugin_helper' - -module Queries - class OvalPoliciesQueryTest < GraphQLQueryTestCase - let(:query) do - <<-GRAPHQL - query { - ovalPolicies { - totalCount - nodes { - id - name - } - } - } - GRAPHQL - end - - let(:data) { result['data']['ovalPolicies'] } - - setup do - FactoryBot.create_list(:oval_policy, 2, :oval_content => FactoryBot.create(:oval_content)) - end - - test 'should fetch oval policies' do - assert_empty result['errors'] - - expected_count = ForemanOpenscap::OvalPolicy.count - - assert_not_equal 0, expected_count - assert_equal expected_count, data['totalCount'] - assert_equal expected_count, data['nodes'].count - end - end -end diff --git a/test/test_plugin_helper.rb b/test/test_plugin_helper.rb index 567285b64..329d146f4 100644 --- a/test/test_plugin_helper.rb +++ b/test/test_plugin_helper.rb @@ -8,8 +8,6 @@ FactoryBot.definition_file_paths << File.join(ForemanPuppet::Engine.root, '/test/factories') if defined?(ForemanPuppet::Engine) FactoryBot.reload -require "#{ForemanOpenscap::Engine.root}/test/fixtures/cve_fixtures" - module ScapClientPuppetclass def puppet_available? Foreman::Plugin.installed?("foreman_puppet") diff --git a/test/unit/oval_host_test.rb b/test/unit/oval_host_test.rb deleted file mode 100644 index a35370ffe..000000000 --- a/test/unit/oval_host_test.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'test_plugin_helper' - -class OvalHostTest < ActiveSupport::TestCase - test 'should show oval policies in enc' do - setup_ansible - - content = FactoryBot.create(:oval_content) - policy = FactoryBot.create(:oval_policy, :oval_content => content) - proxy = FactoryBot.create(:openscap_proxy) - host = FactoryBot.create(:oval_host, :ansible_roles => [@ansible_role], :openscap_proxy => proxy) - facet = FactoryBot.create(:oval_facet, :host => host, :oval_policies => [policy]) - - host_params = host.info["parameters"] - policies = JSON.parse(host_params[@config.policies_param]) - assert_equal 1, policies.length - assert_equal policies.first["id"], policy.id - - assert_equal host_params[@config.port_param], proxy.port.to_s - assert_equal host_params[@config.server_param], proxy.hostname - end - - def setup_ansible - @config = ForemanOpenscap::ClientConfig::Ansible.new(::ForemanOpenscap::OvalPolicy) - @ansible_role = FactoryBot.create(:ansible_role, :name => @config.ansible_role_name) - @port_key = FactoryBot.create( - :ansible_variable, - :key => @config.port_param, - :ansible_role => @ansible_role, - :override => true - ) - @server_key = FactoryBot.create( - :ansible_variable, - :key => @config.server_param, - :ansible_role => @ansible_role, - :override => true - ) - @policies_param = FactoryBot.create( - :ansible_variable, - :key => @config.policies_param, - :ansible_role => @ansible_role, - :override => true, - :default_value => @config.policies_param_default_value - ) - end -end diff --git a/test/unit/oval_policy_test.rb b/test/unit/oval_policy_test.rb deleted file mode 100644 index 6dee5e28c..000000000 --- a/test/unit/oval_policy_test.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'test_plugin_helper' - -class OvalPolicyTest < ActiveSupport::TestCase - setup do - @oval_content = FactoryBot.create(:oval_content) - end - - test "should not create OVAL policy with custom period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'custom', - :cron_line => 'aaa', - :oval_content => @oval_content) - refute policy.save - assert policy.errors[:cron_line].include?("does not consist of 5 parts separated by space") - end - - test "should create OVAL policy with weekly period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'weekly', - :weekday => 'monday', - :oval_content => @oval_content) - assert policy.save - end - - test "should not create OVAL policy with weekly period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'weekly', - :weekday => 'someday', - :oval_content => @oval_content) - refute policy.save - assert policy.errors[:weekday].include?("is not a valid value") - end - - test "should create OVAL policy with monthly period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '1', - :oval_content => @oval_content) - assert policy.save - end - - test "should not create OVAL policy with monthly period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '0', - :oval_content => @oval_content) - refute policy.save - assert policy.errors[:day_of_month].include?("must be between 1 and 31") - end - - test "should not create OVAL policy when attributes do not correspond to selected period in new record" do - policy_0 = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :weekday => 'tuesday', - :cron_line => "0 0 0 0 0", - :oval_content => @oval_content) - policy_1 = ForemanOpenscap::OvalPolicy.new(:name => "test policy", - :period => 'custom', - :weekday => 'tuesday', - :day_of_month => "15", - :oval_content => @oval_content) - refute policy_0.save - refute policy_1.save - end - - test "should update OVAL policy period" do - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '5', - :oval_content => @oval_content) - assert policy.save - policy.period = 'weekly' - policy.weekday = 'monday' - policy.day_of_month = nil - assert policy.save - end - - test "should add and remove hosts for OVAL policy" do - host = FactoryBot.create(:oval_host) - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '5', - :host_ids => [host.id], - :oval_content => @oval_content) - - assert policy.save - assert policy.reload.hosts.include?(host) - - policy.host_ids = [] - assert policy.save - refute policy.reload.hosts.include?(host) - end - - test "should add and remove hostgroups for OVAL policy" do - hostgroup = FactoryBot.create(:hostgroup) - policy = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '5', - :hostgroup_ids => [hostgroup.id], - :oval_content => @oval_content) - assert policy.save - assert policy.reload.hostgroups.include?(hostgroup) - - policy.hostgroup_ids = [] - assert policy.save - refute policy.reload.hostgroups.include?(hostgroup) - end - - test "should add and remove inherited OVAL policy" do - hostgroup = FactoryBot.create(:hostgroup) - host = FactoryBot.create(:oval_host, :hostgroup => hostgroup) - policy_1 = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy", - :period => 'monthly', - :day_of_month => '5', - :hostgroup_ids => [hostgroup.id], - :oval_content => @oval_content) - policy_2 = ForemanOpenscap::OvalPolicy.new(:name => "custom_policy_again", - :period => 'monthly', - :day_of_month => '6', - :host_ids => [host.id], - :oval_content => @oval_content) - assert policy_1.save - assert policy_2.save - - assert host.reload.combined_oval_policies.include?(policy_1) - assert host.combined_oval_policies.include?(policy_2) - - policy_1.hostgroup_ids = [] - assert policy_1.save - refute host.reload.combined_oval_policies.include?(policy_1) - assert host.combined_oval_policies.include?(policy_2) - end -end diff --git a/test/unit/oval_status_test.rb b/test/unit/oval_status_test.rb deleted file mode 100644 index 9b038d2cb..000000000 --- a/test/unit/oval_status_test.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'test_plugin_helper' - -class OvalStatusTest < ActiveSupport::TestCase - setup do - @policy = FactoryBot.create(:oval_policy, :oval_content => FactoryBot.create(:oval_content)) - end - - test 'should have no vulnerabilities' do - host = FactoryBot.create(:oval_host) - FactoryBot.create(:oval_facet, :host => host, :oval_policies => [@policy]) - - status = ForemanOpenscap::OvalStatus.new - status.host = host - assert_equal 0, status.to_status - assert_equal ::HostStatus::Global::OK, status.to_global - assert status.relevant? - end - - test 'should have vulnerabilities with available patch' do - host = FactoryBot.create(:oval_host, :cves => [FactoryBot.create(:cve, :has_errata => false), FactoryBot.create(:cve, :has_errata => true)]) - FactoryBot.create(:oval_facet, :host => host, :oval_policies => [@policy]) - - status = ForemanOpenscap::OvalStatus.new - status.host = host - assert_equal 2, status.to_status - assert_equal ::HostStatus::Global::ERROR, status.to_global - assert status.relevant? - end - - test 'should have vulnerabilities without available patch' do - host = FactoryBot.create(:oval_host, :cves => [FactoryBot.create(:cve, :has_errata => false), FactoryBot.create(:cve, :has_errata => false)]) - FactoryBot.create(:oval_facet, :host => host, :oval_policies => [@policy]) - - status = ForemanOpenscap::OvalStatus.new - status.host = host - assert_equal 1, status.to_status - assert_equal ::HostStatus::Global::WARN, status.to_global - assert status.relevant? - end - - test 'should not be relevant without oval policy' do - host = FactoryBot.create(:oval_host, :cves => [FactoryBot.create(:cve)]) - status = ForemanOpenscap::OvalStatus.new - status.host = host - refute status.relevant? - end -end diff --git a/test/unit/services/oval/cves_test.rb b/test/unit/services/oval/cves_test.rb deleted file mode 100644 index 7d25a7562..000000000 --- a/test/unit/services/oval/cves_test.rb +++ /dev/null @@ -1,81 +0,0 @@ -require 'test_plugin_helper' - -class ForemanOpenscap::Oval::CvesTest < ActiveSupport::TestCase - setup do - @fxs = ForemanOpenscap::CveFixtures.new - @instance = ForemanOpenscap::Oval::Cves.new - end - - test "should add CVEs to host" do - oval_data = create_cve_data @fxs.one - host = FactoryBot.create(:host) - assert_empty host.cves - @instance.create host, oval_data - refute_empty host.cves - - assert_equal host.cves, host.cves.distinct - end - - test "should filter out CVEs that do not affect the host" do - oval_data = create_cve_data @fxs.two - host = FactoryBot.create(:host) - assert_empty host.cves - @instance.create host, oval_data - refute_empty host.cves - - assert_equal host.cves, ForemanOpenscap::Cve.where(:ref_id => @fxs.ids_from(@fxs.res_two)) - end - - test "should update host with a new set of CVEs" do - oval_data = create_cve_data @fxs.one - host = FactoryBot.create(:host) - assert_empty host.cves - @instance.create host, oval_data - refute_empty host.cves - - cve_ids_before = host.reload.cve_ids - new_oval_data = create_cve_data @fxs.two - @instance.create host, new_oval_data - - refute_equal host.reload.cve_ids, cve_ids_before - assert_equal host.cves, ForemanOpenscap::Cve.where(:ref_id => @fxs.ids_from(@fxs.res_two)) - - @fxs.ids_from(@fxs.res_three).map do |ref_id| - refute ForemanOpenscap::Cve.find_by :ref_id => ref_id - end - end - - test "should not delete CVEs associated to another host" do - oval_data = create_cve_data @fxs.one - host = FactoryBot.create(:host) - @instance.create host, oval_data - refute_empty host.cves - - cves_before = host.reload.cves - - oval_data_2 = create_cve_data @fxs.two - host_2 = FactoryBot.create(:host) - @instance.create host_2, oval_data_2 - - assert_equal host.reload.cves, cves_before - end - - test "should not delete CVEs associated to another policy" do - oval_data = create_cve_data [@fxs.res_three] - host = FactoryBot.create(:host) - assert_empty host.cves - @instance.create host, oval_data - refute_empty host.cves - - cve_ids_before = host.reload.cve_ids - new_oval_data = create_cve_data [@fxs.res_four], 2 - @instance.create host, new_oval_data - - refute_equal host.reload.cve_ids, cve_ids_before - assert_equal host.cves, ForemanOpenscap::Cve.where(:ref_id => @fxs.ids_from(@fxs.res_three).concat(@fxs.ids_from(@fxs.res_four))) - end - - def create_cve_data(fixture, policy_id = 1) - { 'oval_results' => fixture, 'oval_policy_id' => policy_id } - end -end diff --git a/test/unit/services/oval/setup_check_test.rb b/test/unit/services/oval/setup_check_test.rb deleted file mode 100644 index 45c06c9ab..000000000 --- a/test/unit/services/oval/setup_check_test.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'test_plugin_helper' - -class ForemanOpenscap::Oval::SetupCheckTest < ActiveSupport::TestCase - test 'should show error message with filled in data' do - check = ::ForemanOpenscap::Oval::SetupCheck.new( - :id => :test_check, - :title => _("Will it pass?"), - :fail_msg => ->(hash) { "There was an error in #{hash[:name]}, you need to #{hash[:action]}" } - ) - - check.fail_with!(:name => 'your engine', :action => 'run') - assert_equal 'There was an error in your engine, you need to run', check.fail_msg - end - - test 'should show error message when it is a string' do - msg = "Do not panic" - check = ::ForemanOpenscap::Oval::SetupCheck.new( - :id => :test_check, - :title => _("Will it pass?"), - :fail_msg => msg - ) - check.fail! - assert_equal msg, check.fail_msg - end - - test 'should not show error message when check not failed' do - check = ::ForemanOpenscap::Oval::SetupCheck.new( - :id => :test_check, - :title => _("Will it pass?"), - :fail_msg => 'foo' - ) - - assert_nil check.fail_msg - check.fail! - assert_not_nil check.fail_msg - end -end diff --git a/test/unit/services/oval/setup_test.rb b/test/unit/services/oval/setup_test.rb deleted file mode 100644 index 072cfa6e9..000000000 --- a/test/unit/services/oval/setup_test.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'test_plugin_helper' - -class ForemanOpenscap::Oval::SetupTest < ActiveSupport::TestCase - setup do - @config = ForemanOpenscap::ClientConfig::Ansible.new(::ForemanOpenscap::OvalPolicy) - end - - test "should fail check when Ansible not available" do - ForemanOpenscap::ClientConfig::Ansible.any_instance.stubs(:available?).returns(false) - - check_collection = ForemanOpenscap::Oval::Setup.new.run - assert check_collection.find_check(:foreman_ansible_present).failed? - assert check_collection.checks.reject { |res| res.id == :foreman_ansible_present }.all?(&:skipped?) - end - - test "should fail check when Ansible role for client not imported" do - ForemanOpenscap::ClientConfig::Ansible.any_instance.stubs(:find_config_item).returns(nil) - - check_collection = ForemanOpenscap::Oval::Setup.new.run - assert check_collection.find_check(:foreman_ansible_present).passed? - assert check_collection.find_check(:foreman_scap_client_role_present).failed? - - assert check_collection.checks - .select { |res| res.id != :foreman_ansible_present && res.id != :foreman_scap_client_role_present } - .all?(&:skipped?) - end - - test "should fail check when required Ansible variables are not imported" do - FactoryBot.create(:ansible_role, :name => @config.ansible_role_name) - check_collection = ForemanOpenscap::Oval::Setup.new.run - assert check_collection.find_check(:foreman_ansible_present).passed? - assert check_collection.find_check(:foreman_scap_client_role_present).passed? - - res = check_collection.find_check(:foreman_scap_client_vars_present) - assert res.failed? - msg = "The following Ansible Variables were not found: foreman_scap_client_oval_policies, foreman_scap_client_port, foreman_scap_client_server, please import them before running this action again." - assert res.fail_msg, msg - assert override_results(check_collection.checks).all?(&:skipped?) - end - - test "should fail check when fails to override a variable" do - role = FactoryBot.create(:ansible_role, :name => @config.ansible_role_name) - FactoryBot.create(:ansible_variable, :key => @config.port_param, :ansible_role => role) - FactoryBot.create(:ansible_variable, :key => @config.server_param, :ansible_role => role) - FactoryBot.create(:ansible_variable, :key => @config.policies_param, :ansible_role => role) - AnsibleVariable.any_instance.stubs(:save).returns(false) - AnsibleVariable.any_instance.stubs(:changed?).returns(true) - check_collection = ForemanOpenscap::Oval::Setup.new.run - assert check_collection.find_check(:foreman_ansible_present).passed? - assert check_collection.find_check(:foreman_scap_client_role_present).passed? - assert check_collection.find_check(:foreman_scap_client_vars_present).passed? - assert override_results(check_collection.checks).all?(&:failed?) - end - - test "should pass all checks" do - role = FactoryBot.create(:ansible_role, :name => @config.ansible_role_name) - port_param = FactoryBot.create(:ansible_variable, :key => @config.port_param, :ansible_role => role) - server_param = FactoryBot.create(:ansible_variable, :key => @config.server_param, :ansible_role => role) - policies_param = FactoryBot.create(:ansible_variable, :key => @config.policies_param, :ansible_role => role) - check_collection = ForemanOpenscap::Oval::Setup.new.run - - [policies_param, port_param, server_param].map(&:reload) - - assert check_collection.all_passed? - - assert @config.policies_param_default_value, policies_param.default_value - assert_equal 'array', policies_param.key_type - refute policies_param.hidden_value? - assert policies_param.override - - refute port_param.value - assert_equal 'integer', port_param.key_type - assert port_param.override - - refute server_param.hidden_value? - assert_equal 'string', server_param.key_type - assert server_param.override - end - - def override_results(checks) - checks.select do |res| - res.id == :foreman_scap_client_server_overriden || - res.id == :foreman_scap_client_port_overriden || - res.id == :foreman_scap_client_policies_overriden - end - end -end diff --git a/webpack/global_index.js b/webpack/global_index.js index afd9a6bc4..d62e51fa3 100644 --- a/webpack/global_index.js +++ b/webpack/global_index.js @@ -1,10 +1,6 @@ import React from 'react'; -import { registerRoutes } from 'foremanReact/routes/RoutingService'; import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill'; import HostKebabItems from './components/HostExtentions/HostKebabItems'; -import routes from './routes/routes'; - -registerRoutes('foreman_openscap', routes); addGlobalFill( 'host-details-kebab', diff --git a/webpack/graphql/mutations/createOvalPolicy.gql b/webpack/graphql/mutations/createOvalPolicy.gql deleted file mode 100644 index b91dff450..000000000 --- a/webpack/graphql/mutations/createOvalPolicy.gql +++ /dev/null @@ -1,22 +0,0 @@ -mutation CreateOvalPolicy($name: String!, $period: String!, $cronLine: String, $ovalContentId: Int!, $hostgroupIds: [Int!]) { - createOvalPolicy(input: {name: $name, period: $period, cronLine: $cronLine, ovalContentId: $ovalContentId, hostgroupIds: $hostgroupIds}) { - ovalPolicy { - name - id - period - cronLine - hostgroups { - nodes { - name - id - } - } - } - checkCollection { - id - errors - failMsg - result - } - } -} \ No newline at end of file diff --git a/webpack/graphql/mutations/deleteOvalContent.gql b/webpack/graphql/mutations/deleteOvalContent.gql deleted file mode 100644 index 7d5d4246b..000000000 --- a/webpack/graphql/mutations/deleteOvalContent.gql +++ /dev/null @@ -1,9 +0,0 @@ -mutation DeleteOvalContent($id: ID!) { - deleteOvalContent(input: { id: $id }) { - id - errors { - path - message - } - } -} diff --git a/webpack/graphql/mutations/deleteOvalPolicy.gql b/webpack/graphql/mutations/deleteOvalPolicy.gql deleted file mode 100644 index 9332a6749..000000000 --- a/webpack/graphql/mutations/deleteOvalPolicy.gql +++ /dev/null @@ -1,9 +0,0 @@ -mutation DeleteOvalPolicy($id: ID!) { - deleteOvalPolicy(input: { id: $id }) { - id - errors { - path - message - } - } -} diff --git a/webpack/graphql/mutations/updateOvalPolicy.gql b/webpack/graphql/mutations/updateOvalPolicy.gql deleted file mode 100644 index 97dba02ff..000000000 --- a/webpack/graphql/mutations/updateOvalPolicy.gql +++ /dev/null @@ -1,14 +0,0 @@ -mutation UpdateOvalPolicy($id: ID!, $name: String, $description: String, $cronLine: String) { - updateOvalPolicy(input:{ id:$id, name:$name, description: $description, cronLine: $cronLine }) { - ovalPolicy { - id - name - description - cronLine - } - errors { - path - message - } - } -} diff --git a/webpack/graphql/queries/currentUserAttributes.gql b/webpack/graphql/queries/currentUserAttributes.gql deleted file mode 100644 index 9349d533f..000000000 --- a/webpack/graphql/queries/currentUserAttributes.gql +++ /dev/null @@ -1,11 +0,0 @@ -fragment CurrentUserAttributes on User { - id - login - admin - permissions { - nodes { - id - name - } - } -} diff --git a/webpack/graphql/queries/cves.gql b/webpack/graphql/queries/cves.gql deleted file mode 100644 index 9469281a8..000000000 --- a/webpack/graphql/queries/cves.gql +++ /dev/null @@ -1,23 +0,0 @@ -#import "./currentUserAttributes.gql" - -query($search: String, $first: Int, $last: Int) { - cves(search: $search, first: $first, last: $last) { - totalCount - nodes { - id - refId - refUrl - hasErrata - definitionId - hosts { - nodes { - id - name - } - } - } - } - currentUser { - ...CurrentUserAttributes - } -} diff --git a/webpack/graphql/queries/hostgroups.gql b/webpack/graphql/queries/hostgroups.gql deleted file mode 100644 index 1cb8ed529..000000000 --- a/webpack/graphql/queries/hostgroups.gql +++ /dev/null @@ -1,14 +0,0 @@ -#import "./currentUserAttributes.gql" - -query($search: String, $first: Int, $last: Int) { - hostgroups(search: $search, first: $first, last: $last) { - totalCount - nodes { - id - name - } - } - currentUser { - ...CurrentUserAttributes - } -} diff --git a/webpack/graphql/queries/ovalContent.gql b/webpack/graphql/queries/ovalContent.gql deleted file mode 100644 index 5bd08d785..000000000 --- a/webpack/graphql/queries/ovalContent.gql +++ /dev/null @@ -1,8 +0,0 @@ -query($id: String!) { - ovalContent(id: $id) { - id - name - url - originalFilename - } -} diff --git a/webpack/graphql/queries/ovalContents.gql b/webpack/graphql/queries/ovalContents.gql deleted file mode 100644 index 0b65491af..000000000 --- a/webpack/graphql/queries/ovalContents.gql +++ /dev/null @@ -1,19 +0,0 @@ -#import "./currentUserAttributes.gql" - -query($first: Int, $last: Int) { - ovalContents(first: $first, last: $last) { - totalCount - nodes { - id - name - url - originalFilename - meta { - canDestroy - } - } - } - currentUser { - ...CurrentUserAttributes - } -} diff --git a/webpack/graphql/queries/ovalPolicies.gql b/webpack/graphql/queries/ovalPolicies.gql deleted file mode 100644 index e48317576..000000000 --- a/webpack/graphql/queries/ovalPolicies.gql +++ /dev/null @@ -1,20 +0,0 @@ -#import "./currentUserAttributes.gql" - -query($first: Int, $last: Int) { - ovalPolicies(first: $first, last: $last) { - totalCount - nodes { - id - name - meta { - canDestroy - } - ovalContent { - name - } - } - } - currentUser { - ...CurrentUserAttributes - } -} diff --git a/webpack/graphql/queries/ovalPolicy.gql b/webpack/graphql/queries/ovalPolicy.gql deleted file mode 100644 index 9bfe32b05..000000000 --- a/webpack/graphql/queries/ovalPolicy.gql +++ /dev/null @@ -1,29 +0,0 @@ -#import "./currentUserAttributes.gql" - -query($id: String!) { - ovalPolicy(id: $id) { - id - name - period - cronLine - weekday - dayOfMonth - description - meta { - canEdit - } - hostgroups { - nodes { - id - descendants { - nodes { - id - } - } - } - } - } - currentUser { - ...CurrentUserAttributes - } -} diff --git a/webpack/helpers/pathsHelper.js b/webpack/helpers/pathsHelper.js deleted file mode 100644 index 5d970f104..000000000 --- a/webpack/helpers/pathsHelper.js +++ /dev/null @@ -1,29 +0,0 @@ -import { decodeModelId } from './globalIdHelper'; - -const experimental = path => `/experimental${path}`; - -const showPath = path => `${path}/:id`; -const newPath = path => `${path}/new`; - -export const modelPath = (basePath, model) => - `${basePath}/${decodeModelId(model)}`; - -// react-router uses path-to-regexp, should we use it as well in a future? -// https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#compile-reverse-path-to-regexp -export const resolvePath = (path, params) => - Object.entries(params).reduce( - (memo, [key, value]) => memo.replace(key, value), - path - ); - -export const ovalContentsApiPath = '/api/v2/compliance/oval_contents'; - -export const ovalContentsPath = experimental('/compliance/oval_contents'); -export const ovalContentsShowPath = showPath(ovalContentsPath); -export const ovalContentsNewPath = newPath(ovalContentsPath); -export const ovalPoliciesPath = experimental('/compliance/oval_policies'); -export const ovalPoliciesShowPath = `${showPath(ovalPoliciesPath)}/:tab?`; -export const ovalPoliciesNewPath = newPath(ovalPoliciesPath); -export const hostsPath = '/hosts'; -export const newJobPath = newPath('/job_invocations'); -export const hostsShowPath = showPath(hostsPath); diff --git a/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js b/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js deleted file mode 100644 index 313149ed6..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsIndex.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/client'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import IndexLayout from '../../../components/IndexLayout'; -import LinkButton from '../../../components/LinkButton'; -import OvalContentsTable from './OvalContentsTable'; -import { ovalContentsNewPath } from '../../../helpers/pathsHelper'; -import { - useParamsToVars, - useCurrentPagination, -} from '../../../helpers/pageParamsHelper'; - -import { submitDelete, prepareMutation } from '../../../helpers/mutationHelper'; -import ovalContentsQuery from '../../../graphql/queries/ovalContents.gql'; -import deleteOvalContentMutation from '../../../graphql/mutations/deleteOvalContent.gql'; - -const OvalContentsIndex = props => { - const useFetchFn = componentProps => - useQuery(ovalContentsQuery, { - variables: useParamsToVars(componentProps.history), - }); - - const renameData = data => ({ - ovalContents: data.ovalContents.nodes, - totalCount: data.ovalContents.totalCount, - }); - - const pagination = useCurrentPagination(props.history); - - return ( - - - } - shouldRefetch={props.location?.state?.refreshOvalContents} - /> - - ); -}; - -OvalContentsIndex.propTypes = { - history: PropTypes.object.isRequired, - showToast: PropTypes.func.isRequired, - location: PropTypes.object.isRequired, -}; - -export default OvalContentsIndex; diff --git a/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js b/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js deleted file mode 100644 index 5366f18c3..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/OvalContentsTable.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; -import { Button } from '@patternfly/react-core'; - -import withLoading from '../../../components/withLoading'; -import withDeleteModal from '../../../components/withDeleteModal'; -import IndexTable from '../../../components/IndexTable'; -import { - ovalContentsNewPath, - ovalContentsPath, - modelPath, -} from '../../../helpers/pathsHelper'; - -import { linkCell } from '../../../helpers/tableHelper'; - -const OvalContentsTable = props => { - const columns = [ - { title: __('Name') }, - { title: __('URL') }, - { title: __('Original File Name') }, - ]; - - const rows = props.ovalContents.map(ovalContent => ({ - cells: [ - { - title: linkCell( - modelPath(ovalContentsPath, ovalContent), - ovalContent.name - ), - }, - { title: ovalContent.url || '' }, - { title: ovalContent.originalFilename || '' }, - ], - ovalContent, - })); - - const actionResolver = (rowData, rest) => { - const actions = []; - if (rowData.ovalContent.meta.canDestroy) { - actions.push({ - title: __('Delete OVAL Content'), - onClick: (event, rowId, rData, extra) => { - props.toggleModal(rData.ovalContent); - }, - }); - } - return actions; - }; - - const createBtn = ( - - ); - - return ( - - ); -}; - -OvalContentsTable.propTypes = { - ovalContents: PropTypes.array.isRequired, - pagination: PropTypes.object.isRequired, - totalCount: PropTypes.number.isRequired, - history: PropTypes.object.isRequired, - toggleModal: PropTypes.func.isRequired, -}; - -export default withLoading(withDeleteModal(OvalContentsTable)); diff --git a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js b/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js deleted file mode 100644 index eb8a8e4a9..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.fixtures.js +++ /dev/null @@ -1,105 +0,0 @@ -import { admin } from '../../../../testHelper'; - -import ovalContentsQuery from '../../../../graphql/queries/ovalContents.gql'; -import deleteOvalContent from '../../../../graphql/mutations/deleteOvalContent.gql'; - -export const firstCall = { - data: { - ovalContents: { - totalCount: 5, - nodes: [ - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z', - name: 'ansible OVAL content', - url: - 'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2', - originalFilename: '', - meta: { canDestroy: true }, - }, - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00', - name: 'dotnet OVAL content', - url: - 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2', - originalFilename: '', - meta: { canDestroy: true }, - }, - ], - }, - currentUser: admin, - }, -}; - -export const secondCall = { - data: { - ovalContents: { - totalCount: 4, - nodes: [ - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00', - name: 'dotnet OVAL content', - url: - 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2', - originalFilename: '', - meta: { canDestroy: true }, - }, - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC03', - name: 'jboss OVAL content', - url: '', - originalFilename: 'jboss.oval.xml.bz2', - meta: { canDestroy: true }, - }, - ], - }, - currentUser: admin, - }, -}; - -export const deleteMockFactory = (first, second, errors = null) => { - let called = false; - - const deleteMocks = [ - { - request: { - query: deleteOvalContent, - variables: { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z', - }, - }, - result: { - data: { - deleteOvalContent: { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z', - errors, - }, - }, - }, - }, - { - request: { - query: ovalContentsQuery, - variables: { - first: 2, - last: 2, - }, - }, - newData: () => { - if (called && !errors) { - return second; - } else if (called && errors) { - return first; - } - called = true; - return first; - }, - }, - ]; - return deleteMocks; -}; - -export const pageParamsHistoryMock = { - location: { - search: '?page=1&perPage=2', - }, -}; diff --git a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js b/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js deleted file mode 100644 index e27c73a22..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsDestroy.test.js +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; - -import OvalContentsIndex from '../OvalContentsIndex'; -import { - withRouter, - withRedux, - withMockedProvider, - tick, - historyMock, -} from '../../../../testHelper'; -import { mocks, noDeleteMocks } from './OvalContentsIndex.fixtures'; -import { - firstCall, - secondCall, - deleteMockFactory, - pageParamsHistoryMock, -} from './OvalContentsDestroy.fixtures'; - -const TestComponent = withRouter( - withRedux(withMockedProvider(OvalContentsIndex)) -); - -describe('OvalContentsIndex', () => { - it('should open and close delete modal', async () => { - render( - - ); - await waitFor(tick); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Content')); - await waitFor(tick); - expect( - screen.getByText('Are you sure you want to delete ansible OVAL content?') - ).toBeInTheDocument(); - userEvent.click(screen.getByText('Cancel')); - await waitFor(tick); - expect( - screen.queryByText( - 'Are you sure you want to delete ansible OVAL content?' - ) - ).not.toBeInTheDocument(); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - }); - it('should delete OVAL content', async () => { - const mocked = deleteMockFactory(firstCall, secondCall); - const showToast = jest.fn(); - render( - - ); - await waitFor(tick); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Content')); - await waitFor(tick); - userEvent.click(screen.getByText('Confirm')); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'success', - message: 'OVAL Content successfully deleted.', - }); - await waitFor(tick); - expect(screen.queryByText('ansible OVAL content')).not.toBeInTheDocument(); - expect(screen.getByText('jboss OVAL content')).toBeInTheDocument(); - }); - it('should show error when deleting OVAL content fails', async () => { - const showToast = jest.fn(); - render( - - ); - await waitFor(tick); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Content')); - await waitFor(tick); - userEvent.click(screen.getByText('Confirm')); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'error', - message: - 'There was a following error when deleting OVAL Content: is used by first policy, is used by second policy', - }); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - expect(screen.queryByText('jboss OVAL content')).not.toBeInTheDocument(); - }); - it('should not show delete button when user does not have delete permissions', async () => { - render( - - ); - await waitFor(tick); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'Actions' }) - ).not.toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js b/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js deleted file mode 100644 index 37f948adc..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures.js +++ /dev/null @@ -1,127 +0,0 @@ -import ovalContentsQuery from '../../../../graphql/queries/ovalContents.gql'; -import { ovalContentsPath } from '../../../../helpers/pathsHelper'; -import { - mockFactory, - admin, - intruder, - userFactory, -} from '../../../../testHelper'; - -const ovalContentMockFactory = mockFactory('ovalContents', ovalContentsQuery); - -const viewer = userFactory('viewer', [ - { - __typename: 'Permission', - id: 'MDE6UGVybWlzc2lvbi0yOTY=', - name: 'view_oval_contents', - }, -]); - -const firstContent = (meta = { canDestroy: true }) => ({ - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0z', - name: 'ansible OVAL content', - url: - 'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2', - originalFilename: '', - meta, -}); - -const secondContent = (meta = { canDestroy: true }) => ({ - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00', - name: 'dotnet OVAL content', - url: 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2', - originalFilename: '', - meta, -}); - -const thirdContent = (meta = { canDestroy: true }) => ({ - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC03', - name: 'jboss OVAL content', - url: '', - originalFilename: 'jboss.oval.xml.bz2', - meta, -}); - -const fourthContent = (meta = { canDestroy: true }) => ({ - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC0zMw==', - name: 'openshift OVAL content', - url: '', - originalFilename: 'openshift.oval.xml.bz2', - meta, -}); - -const ovalContentNodes = [ - firstContent(), - secondContent(), - thirdContent(), - fourthContent(), -]; - -export const ovalContents = { - totalCount: ovalContentNodes.length, - nodes: ovalContentNodes, -}; - -export const mocks = ovalContentMockFactory( - { first: 20, last: 20 }, - { - totalCount: 4, - nodes: [firstContent(), secondContent(), thirdContent(), fourthContent()], - }, - { currentUser: admin } -); - -export const unpagedMocks = ovalContentMockFactory({}, ovalContents, { - currentUser: admin, -}); - -export const paginatedMocks = ovalContentMockFactory( - { first: 10, last: 5 }, - { totalCount: 7, nodes: [secondContent(), fourthContent()] }, - { currentUser: admin } -); - -export const emptyMocks = ovalContentMockFactory( - { first: 20, last: 20 }, - { totalCount: 0, nodes: [] }, - { currentUser: admin } -); -export const errorMocks = ovalContentMockFactory( - { first: 20, last: 20 }, - { totalCount: 0, nodes: [] }, - { errors: [{ message: 'Something very bad happened.' }], currentUser: admin } -); - -export const viewerMocks = ovalContentMockFactory( - { first: 20, last: 20 }, - ovalContents, - { currentUser: viewer } -); - -export const unauthorizedMocks = ovalContentMockFactory( - { first: 20, last: 20 }, - ovalContents, - { currentUser: intruder } -); - -export const noDeleteMocks = ovalContentMockFactory( - { first: 20, last: 20 }, - { - totalCount: 2, - nodes: [ - firstContent({ canDestroy: false }), - secondContent({ canDestroy: false }), - ], - }, - { currentUser: admin } -); - -export const pushMock = jest.fn(); - -export const pagePaginationHistoryMock = { - location: { - search: '?page=2&perPage=5', - pathname: ovalContentsPath, - }, - push: pushMock, -}; diff --git a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js b/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js deleted file mode 100644 index c004f8e68..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.test.js +++ /dev/null @@ -1,89 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; - -import OvalContentsIndex from '../'; - -import { - withRouter, - withRedux, - withMockedProvider, - tick, - historyMock, -} from '../../../../testHelper'; - -import { - mocks, - emptyMocks, - errorMocks, - viewerMocks, - unauthorizedMocks, -} from './OvalContentsIndex.fixtures'; - -const TestComponent = withRedux( - withRouter(withMockedProvider(OvalContentsIndex)) -); - -describe('OvalContentsIndex', () => { - it('should load page', async () => { - render(); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - expect( - screen.getByText( - 'http://oval-content-source/security/data/oval/ansible-2-including-unpatched.oval.xml.bz2' - ) - ).toBeInTheDocument(); - expect(screen.getByText('openshift OVAL content')).toBeInTheDocument(); - expect(screen.getByText('openshift.oval.xml.bz2')).toBeInTheDocument(); - }); - it('should show empty state', async () => { - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('No OVAL Contents found.')).toBeInTheDocument(); - }); - it('should show errors', async () => { - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect( - screen.getByText('Something very bad happened.') - ).toBeInTheDocument(); - expect(screen.getByText('Error!')).toBeInTheDocument(); - }); - it('should load page for user with permissions', async () => { - render( - - ); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('ansible OVAL content')).toBeInTheDocument(); - }); - it('should not load page for user without permissions', async () => { - render( - - ); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.queryByText('ansible OVAL content')).not.toBeInTheDocument(); - expect( - screen.getByText( - 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_contents.' - ) - ).toBeInTheDocument(); - expect(screen.getByText('Permission denied')).toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalContents/OvalContentsIndex/index.js b/webpack/routes/OvalContents/OvalContentsIndex/index.js deleted file mode 100644 index ff3d1f1a2..000000000 --- a/webpack/routes/OvalContents/OvalContentsIndex/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; -import { showToast } from '../../../helpers/toastHelper'; - -import OvalContentsIndex from './OvalContentsIndex'; - -const WrappedOvalContentsIndex = props => { - const dispatch = useDispatch(); - - return ; -}; - -export default WrappedOvalContentsIndex; diff --git a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js b/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js deleted file mode 100644 index 07b666870..000000000 --- a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.js +++ /dev/null @@ -1,138 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; -import { Formik, Field as FormikField } from 'formik'; - -import { - Form as PfForm, - ActionGroup, - Button, - FileUpload, - FormGroup, - Radio, - Spinner, -} from '@patternfly/react-core'; -import { - onSubmit, - createValidationSchema, - validateFile, - submitDisabled, -} from './OvalContentsNewHelper'; -import LinkButton from '../../../components/LinkButton'; -import IndexLayout from '../../../components/IndexLayout'; -import { TextField } from '../../../helpers/formFieldsHelper'; -import { ovalContentsPath } from '../../../helpers/pathsHelper'; - -import './OvalContentsNew.scss'; - -const OvalContentsNew = props => { - const [file, setFile] = useState(null); - const [fileTouched, setFileTouched] = useState(false); - const [fileFromUrl, setFileFromUrl] = useState(true); - - const handleFileChange = (value, filename, event) => { - setFile(value); - setFileTouched(true); - }; - - return ( - - - onSubmit( - values, - actions, - props.showToast, - props.history, - fileFromUrl, - file - ) - } - initialValues={{ name: '', url: '' }} - validationSchema={createValidationSchema(fileFromUrl)} - > - {formProps => ( - - - - { - setFileFromUrl(true); - // Force validations to run by setting the same value. - // Workaround for https://github.com/formium/formik/issues/1755 - formProps.setFieldValue(formProps.values.url); - }} - label={__('OVAL Content from URL')} - /> - { - setFileFromUrl(false); - const filtered = Object.entries(formProps.errors).filter( - ([key, value]) => key !== 'url' - ); - formProps.setErrors(Object.fromEntries(filtered)); - }} - label={__('OVAL Content from file')} - /> - - {!fileFromUrl ? ( - - - - ) : ( - - )} - - - - {formProps.isSubmitting ? : null} - - - )} - - - ); -}; - -OvalContentsNew.propTypes = { - showToast: PropTypes.func.isRequired, - history: PropTypes.object.isRequired, -}; - -export default OvalContentsNew; diff --git a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss b/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss deleted file mode 100644 index d71d1b629..000000000 --- a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNew.scss +++ /dev/null @@ -1,3 +0,0 @@ -#scap-file-source-url, #scap-file-source-file { - margin: 0; -} diff --git a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js b/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js deleted file mode 100644 index ee69caf42..000000000 --- a/webpack/routes/OvalContents/OvalContentsNew/OvalContentsNewHelper.js +++ /dev/null @@ -1,73 +0,0 @@ -import * as Yup from 'yup'; - -import api from 'foremanReact/redux/API/API'; -import { prepareErrors } from 'foremanReact/redux/actions/common/forms'; -import { sprintf, translate as __ } from 'foremanReact/common/I18n'; -import { - ovalContentsPath, - ovalContentsApiPath, -} from '../../../helpers/pathsHelper'; - -export const submitForm = (params, actions) => { - const headers = { - 'Content-Type': 'multipart/form-data', - }; - return api.post(ovalContentsApiPath, params, headers); -}; - -export const onSubmit = async ( - values, - actions, - showToast, - history, - fileFromUrl, - file -) => { - const formData = new FormData(); - if (fileFromUrl) { - formData.append('oval_content[url]', values.url); - } else { - formData.append('oval_content[scap_file]', file); - } - formData.append('oval_content[name]', values.name); - try { - await submitForm(formData, actions); - history.push(ovalContentsPath, { refreshOvalContents: true }); - showToast({ - type: 'success', - message: sprintf(__('OVAL Content %s successfully created'), values.name), - }); - } catch (error) { - onError(error, actions, showToast); - } -}; - -const onError = (error, actions, showToast) => { - actions.setSubmitting(false); - if (error.response?.status === 422) { - actions.setErrors(prepareErrors(error?.response?.data?.error?.errors, {})); - } else { - showToast({ - type: 'error', - message: __( - 'Unknown error when submitting data, please try again later.' - ), - }); - } -}; - -export const validateFile = (file, touched) => { - if (!touched) { - return 'default'; - } - return file ? 'success' : 'error'; -}; - -export const submitDisabled = (formProps, file, fileFromUrl) => - formProps.isSubmitting || !formProps.isValid || (!fileFromUrl && !file); - -export const createValidationSchema = contentFromUrl => - Yup.object().shape({ - name: Yup.string().required("can't be blank"), - ...(contentFromUrl && { url: Yup.string().required("can't be blank") }), - }); diff --git a/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js b/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js deleted file mode 100644 index dd68e77c4..000000000 --- a/webpack/routes/OvalContents/OvalContentsNew/__tests__/OvalContentsNew.test.js +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; - -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import '@testing-library/jest-dom'; -import api from 'foremanReact/redux/API/API'; - -import OvalContentsNew from '../OvalContentsNew'; -import { withRouter, withRedux, tick } from '../../../../testHelper'; -import { ovalContentsPath } from '../../../../helpers/pathsHelper'; - -jest.mock('foremanReact/redux/API/API', () => ({ post: jest.fn() })); - -const TestComponent = withRouter(withRedux(OvalContentsNew)); - -describe('OvalContentsNew', () => { - it('should create with content from URL', async () => { - const pushMock = jest.fn(); - const toastMock = jest.fn(); - - api.post.mockImplementation(() => Promise.resolve()); - - render( - - ); - expect(screen.getByText('Name')).toBeInTheDocument(); - expect(screen.getByText('OVAL Content Source')).toBeInTheDocument(); - expect(screen.getByText('URL')).toBeInTheDocument(); - expect(screen.queryByText('File')).not.toBeInTheDocument(); - expect(screen.getByText('Submit')).toBeDisabled(); - userEvent.type(screen.getByLabelText('name'), 'test content'); - await waitFor(tick); - expect(screen.getByText('Submit')).toBeDisabled(); - userEvent.type( - screen.getByLabelText(/url/), - 'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2' - ); - await waitFor(tick); - expect(screen.getByText('Submit')).not.toBeDisabled(); - userEvent.click(screen.getByText('Submit')); - await waitFor(tick); - expect(pushMock).toHaveBeenCalledWith(ovalContentsPath, { - refreshOvalContents: true, - }); - expect(toastMock).toHaveBeenCalledWith({ - type: 'success', - message: 'OVAL Content test content successfully created', - }); - }); - it('should show resource errors', async () => { - const pushMock = jest.fn(); - const toastMock = jest.fn(); - api.post.mockImplementation(() => { - // eslint-disable-next-line no-throw-literal - throw { - response: { - status: 422, - data: { error: { errors: { name: ['has already been taken'] } } }, - }, - }; - }); - - render( - - ); - userEvent.type(screen.getByLabelText('name'), 'test content'); - userEvent.type( - screen.getByLabelText(/url/), - 'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2' - ); - await waitFor(tick); - userEvent.click(screen.getByText('Submit')); - await waitFor(tick); - expect(pushMock).not.toHaveBeenCalled(); - expect(screen.getByText('has already been taken')).toBeInTheDocument(); - }); - it('should show error toast on unexpected error', async () => { - const pushMock = jest.fn(); - const toastMock = jest.fn(); - - api.post.mockImplementation(() => { - // eslint-disable-next-line no-throw-literal - throw { response: { status: 500 } }; - }); - - render( - - ); - userEvent.type(screen.getByLabelText('name'), 'test content'); - userEvent.type( - screen.getByLabelText(/url/), - 'http://oval-content-source.org/security/data/oval/v2/CentOS7/ansible-2.9.oval.xml.bz2' - ); - await waitFor(tick); - userEvent.click(screen.getByText('Submit')); - await waitFor(tick); - expect(pushMock).not.toHaveBeenCalled(); - expect(screen.getByText('Submit')).not.toBeDisabled(); - expect(toastMock).toHaveBeenCalledWith({ - type: 'error', - message: 'Unknown error when submitting data, please try again later.', - }); - }); -}); diff --git a/webpack/routes/OvalContents/OvalContentsNew/index.js b/webpack/routes/OvalContents/OvalContentsNew/index.js deleted file mode 100644 index 0a8f2494d..000000000 --- a/webpack/routes/OvalContents/OvalContentsNew/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; -import { showToast } from '../../../helpers/toastHelper'; - -import OvalContentsNew from './OvalContentsNew'; - -const WrappedOvalContentsNew = props => { - const dispatch = useDispatch(); - - return ; -}; - -export default WrappedOvalContentsNew; diff --git a/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js b/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js deleted file mode 100644 index 094a94b10..000000000 --- a/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Helmet } from 'react-helmet'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import { - Grid, - GridItem, - TextContent, - Text, - TextVariants, -} from '@patternfly/react-core'; - -import withLoading from '../../../components/withLoading'; - -const OvalContentsShow = ({ ovalContent }) => { - let contentSource; - if (ovalContent.url) { - contentSource = ( - - {__('URL')} - {ovalContent.url || ''} - - ); - } else { - contentSource = ( - - {__('File')} - - {ovalContent.originalFilename || ''} - - - ); - } - - return ( - - - {`${ovalContent.name} | ${__('OVAL Content')}`} - - - - {ovalContent.name} - - - - - {__('Name')} - {ovalContent.name} - {contentSource} - - - - - ); -}; - -OvalContentsShow.propTypes = { - ovalContent: PropTypes.object.isRequired, -}; - -export default withLoading(OvalContentsShow); diff --git a/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js b/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js deleted file mode 100644 index c6e5fcca6..000000000 --- a/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShow.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import '@testing-library/jest-dom'; -import { render, screen, waitFor } from '@testing-library/react'; - -import { withMockedProvider, tick } from '../../../testHelper'; -import ovalContentQuery from '../../../graphql/queries/ovalContent.gql'; -import OvalContentsShow from './'; - -const TestComponent = withMockedProvider(OvalContentsShow); - -const matchMock = { params: { id: 4 } }; -const name = 'dotnet OVAL content'; -const url = - 'http://oval-content-source/security/data/oval/dotnet-2.2.oval.xml.bz2'; -const id = 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsQ29udGVudC00'; - -const mocks = [ - { - request: { - query: ovalContentQuery, - variables: { id }, - }, - result: { - data: { - ovalContent: { - id, - name, - url, - originalFilename: '', - }, - }, - }, - }, -]; - -describe('OVAL Contents show page', () => { - it('should show OVAL Content', async () => { - render(); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getAllByText(name).length === 2).toBeTruthy(); - expect(screen.getByText(url)).toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js b/webpack/routes/OvalContents/OvalContentsShow/OvalContentsShowHelper.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/webpack/routes/OvalContents/OvalContentsShow/index.js b/webpack/routes/OvalContents/OvalContentsShow/index.js deleted file mode 100644 index 70366d4c0..000000000 --- a/webpack/routes/OvalContents/OvalContentsShow/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/client'; - -import { translate as __ } from 'foremanReact/common/I18n'; - -import OvalContentsShow from './OvalContentsShow'; -import { encodeId } from '../../../helpers/globalIdHelper'; - -import ovalContent from '../../../graphql/queries/ovalContent.gql'; - -const WrappedOvalContentsShow = props => { - const id = encodeId('ForemanOpenscap::OvalContent', props.match.params.id); - - const useFetchFn = componentProps => - useQuery(ovalContent, { variables: { id } }); - - const renameData = data => ({ ovalContent: data.ovalContent }); - - return ( - - ); -}; - -WrappedOvalContentsShow.propTypes = { - match: PropTypes.object.isRequired, -}; - -export default WrappedOvalContentsShow; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js deleted file mode 100644 index 875e31fde..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesIndex.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/client'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import OvalPoliciesTable from './OvalPoliciesTable'; -import { submitDelete, prepareMutation } from '../../../helpers/mutationHelper'; - -import IndexLayout from '../../../components/IndexLayout'; - -import { - useParamsToVars, - useCurrentPagination, -} from '../../../helpers/pageParamsHelper'; -import policiesQuery from '../../../graphql/queries/ovalPolicies.gql'; -import deleteOvalPolicyMutation from '../../../graphql/mutations/deleteOvalPolicy.gql'; - -const OvalPoliciesIndex = props => { - const useFetchFn = componentProps => - useQuery(policiesQuery, { - variables: useParamsToVars(componentProps.history), - }); - - const renameData = data => ({ - policies: data.ovalPolicies.nodes, - totalCount: data.ovalPolicies.totalCount, - }); - - const pagination = useCurrentPagination(props.history); - - return ( - - - - ); -}; - -OvalPoliciesIndex.propTypes = { - history: PropTypes.object.isRequired, - showToast: PropTypes.func.isRequired, -}; - -export default OvalPoliciesIndex; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js deleted file mode 100644 index 5e6c8f2f8..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/OvalPoliciesTable.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Button } from '@patternfly/react-core'; - -import { translate as __ } from 'foremanReact/common/I18n'; - -import IndexTable from '../../../components/IndexTable'; -import withLoading from '../../../components/withLoading'; -import withDeleteModal from '../../../components/withDeleteModal'; - -import { linkCell } from '../../../helpers/tableHelper'; -import { - ovalPoliciesPath, - modelPath, - ovalPoliciesNewPath, -} from '../../../helpers/pathsHelper'; - -const OvalPoliciesTable = props => { - const columns = [{ title: __('Name') }, { title: __('OVAL Content') }]; - - const rows = props.policies.map(policy => ({ - cells: [ - { title: linkCell(modelPath(ovalPoliciesPath, policy), policy.name) }, - { title: policy.ovalContent.name }, - ], - policy, - })); - - const actionResolver = (rowData, rest) => { - const actions = []; - if (rowData.policy.meta.canDestroy) { - actions.push({ - title: __('Delete OVAL Policy'), - onClick: (event, rowId, rData, extra) => { - props.toggleModal(rData.policy); - }, - }); - } - return actions; - }; - - const createBtn = ( - - ); - - return ( - - ); -}; - -OvalPoliciesTable.propTypes = { - policies: PropTypes.array.isRequired, - pagination: PropTypes.object.isRequired, - totalCount: PropTypes.number.isRequired, - history: PropTypes.object.isRequired, - toggleModal: PropTypes.func.isRequired, -}; - -export default withLoading(withDeleteModal(OvalPoliciesTable)); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js deleted file mode 100644 index 85a0afc6f..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.fixtures.js +++ /dev/null @@ -1,101 +0,0 @@ -import policiesQuery from '../../../../graphql/queries/ovalPolicies.gql'; -import deleteOvalPolicy from '../../../../graphql/mutations/deleteOvalPolicy.gql'; - -import { admin } from '../../../../testHelper'; - -export const firstCall = { - data: { - ovalPolicies: { - totalCount: 5, - nodes: [ - { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz', - name: 'first policy', - meta: { canDestroy: true }, - ovalContent: { name: 'foo' }, - }, - { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ0', - name: 'second policy', - meta: { canDestroy: true }, - ovalContent: { name: 'foo' }, - }, - ], - }, - currentUser: admin, - }, -}; - -export const secondCall = { - data: { - ovalPolicies: { - totalCount: 4, - nodes: [ - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ0', - name: 'second policy', - meta: { canDestroy: true }, - ovalContent: { name: 'foo' }, - }, - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQ1', - name: 'third policy', - meta: { canDestroy: true }, - ovalContent: { name: 'foo' }, - }, - ], - }, - currentUser: admin, - }, -}; - -export const deleteMockFactory = (first, second, errors = null) => { - let called = false; - - const deleteMocks = [ - { - request: { - query: deleteOvalPolicy, - variables: { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz', - }, - }, - result: { - data: { - deleteOvalPolicy: { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQz', - errors, - }, - }, - }, - }, - { - request: { - query: policiesQuery, - variables: { - first: 2, - last: 2, - }, - }, - newData: () => { - if (called && !errors) { - return second; - } else if (called && errors) { - return first; - } - called = true; - return first; - }, - }, - ]; - return deleteMocks; -}; - -export const pageParamsHistoryMock = { - location: { - search: '?page=1&perPage=2', - }, -}; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js deleted file mode 100644 index 148e82b6f..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesDestroy.test.js +++ /dev/null @@ -1,117 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; - -import OvalPoliciesIndex from '../OvalPoliciesIndex'; -import { - withRouter, - withRedux, - withMockedProvider, - tick, - historyMock, -} from '../../../../testHelper'; -import { mocks, noDeleteMocks } from './OvalPoliciesIndex.fixtures'; -import { - firstCall, - secondCall, - deleteMockFactory, - pageParamsHistoryMock, -} from './OvalPoliciesDestroy.fixtures'; - -const TestComponent = withRouter( - withRedux(withMockedProvider(OvalPoliciesIndex)) -); - -describe('OvalPoliciesIndex', () => { - it('should open and close delete modal', async () => { - render( - - ); - await waitFor(tick); - expect(screen.getByText('first policy')).toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Policy')); - await waitFor(tick); - expect( - screen.getByText('Are you sure you want to delete first policy?') - ).toBeInTheDocument(); - userEvent.click(screen.getByText('Cancel')); - await waitFor(tick); - expect( - screen.queryByText('Are you sure you want to delete first policy?') - ).not.toBeInTheDocument(); - expect(screen.getByText('first policy')).toBeInTheDocument(); - }); - it('should delete OVAL policy', async () => { - const showToast = jest.fn(); - render( - - ); - await waitFor(tick); - expect(screen.getByText('first policy')).toBeInTheDocument(); - expect(screen.queryByText('third policy')).not.toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Policy')); - await waitFor(tick); - userEvent.click(screen.getByText('Confirm')); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'success', - message: 'OVAL policy was successfully deleted.', - }); - await waitFor(tick); - expect(screen.queryByText('first policy')).not.toBeInTheDocument(); - expect(screen.getByText('third policy')).toBeInTheDocument(); - }); - it('should show error when deleting OVAL policy fails', async () => { - const showToast = jest.fn(); - render( - - ); - await waitFor(tick); - expect(screen.getByText('first policy')).toBeInTheDocument(); - expect(screen.queryByText('third policy')).not.toBeInTheDocument(); - userEvent.click(screen.getAllByRole('button', { name: 'Actions' })[0]); - userEvent.click(screen.getByText('Delete OVAL Policy')); - await waitFor(tick); - userEvent.click(screen.getByText('Confirm')); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'error', - message: - 'There was a following error when deleting OVAL policy: cannot do it, will not do it', - }); - expect(screen.getByText('first policy')).toBeInTheDocument(); - expect(screen.queryByText('third policy')).not.toBeInTheDocument(); - }); - it('should not show delete button when user does not have delete permissions', async () => { - render( - - ); - await waitFor(tick); - expect(screen.getByText('first policy')).toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'Actions' }) - ).not.toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js deleted file mode 100644 index a7c6d96e3..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.fixtures.js +++ /dev/null @@ -1,111 +0,0 @@ -import policiesQuery from '../../../../graphql/queries/ovalPolicies.gql'; -import { ovalPoliciesPath } from '../../../../helpers/pathsHelper'; -import { - mockFactory, - admin, - intruder, - userFactory, -} from '../../../../testHelper'; - -const policiesMockFactory = mockFactory('ovalPolicies', policiesQuery); - -export const pushMock = jest.fn(); - -export const pageParamsHistoryMock = { - location: { - search: '?page=2&perPage=5', - pathname: ovalPoliciesPath, - }, - push: pushMock, -}; - -const viewer = userFactory('viewer', [ - { - __typename: 'Permission', - id: 'MDE6UGVybWlzc2lvbi0yOTY=', - name: 'view_oval_policies', - }, -]); - -const firstPolicy = (meta = { canDestroy: true }) => ({ - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTE=', - name: 'first policy', - meta, - ovalContent: { name: 'first content' }, -}); -const secondPolicy = (meta = { canDestroy: true }) => ({ - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQw', - name: 'second policy', - meta, - ovalContent: { name: 'second content' }, -}); -const policiesData = { - totalCount: 2, - nodes: [firstPolicy(), secondPolicy()], -}; - -export const mocks = policiesMockFactory( - { first: 20, last: 20 }, - policiesData, - { currentUser: admin } -); -export const pageParamsMocks = policiesMockFactory( - { first: 10, last: 5 }, - { - totalCount: 7, - nodes: [ - { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQx', - name: 'sixth policy', - meta: { canDestroy: true }, - ovalContent: { name: 'sixth content' }, - }, - { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTQy', - name: 'seventh policy', - meta: { canDestroy: true }, - ovalContent: { name: 'seventh content' }, - }, - ], - }, - { currentUser: admin } -); - -export const emptyMocks = policiesMockFactory( - { first: 20, last: 20 }, - { totalCount: 0, nodes: [] }, - { currentUser: admin } -); -export const errorMocks = policiesMockFactory( - { first: 20, last: 20 }, - { totalCount: 0, nodes: [] }, - { - errors: [{ message: 'Something very bad happened.', path: 'base' }], - currentUser: admin, - } -); -export const viewerMocks = policiesMockFactory( - { first: 20, last: 20 }, - policiesData, - { currentUser: viewer } -); -export const unauthorizedMocks = policiesMockFactory( - { first: 20, last: 20 }, - policiesData, - { currentUser: intruder } -); -export const noDeleteMocks = policiesMockFactory( - { first: 20, last: 20 }, - { - totalCount: 2, - nodes: [ - firstPolicy({ canDestroy: false }), - secondPolicy({ canDestroy: false }), - ], - }, - { currentUser: admin } -); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js deleted file mode 100644 index 661becdcd..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/__tests__/OvalPoliciesIndex.test.js +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react'; -import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; - -import { - withMockedProvider, - withRouter, - withRedux, - tick, - historyMock, -} from '../../../../testHelper'; - -import { - mocks, - emptyMocks, - errorMocks, - viewerMocks, - unauthorizedMocks, -} from './OvalPoliciesIndex.fixtures'; - -import OvalPoliciesIndex from '../index'; - -const TestComponent = withRouter( - withRedux(withMockedProvider(OvalPoliciesIndex)) -); - -describe('OvalPoliciesIndex', () => { - it('should load page', async () => { - render(); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.getByText('first policy')).toBeInTheDocument(); - expect(screen.getByText('second policy')).toBeInTheDocument(); - expect(screen.getByText('first content')).toBeInTheDocument(); - expect(screen.getByText('second content')).toBeInTheDocument(); - - expect(screen.getByText('first policy').closest('a')).toHaveAttribute( - 'href', - '/experimental/compliance/oval_policies/1' - ); - expect(screen.getByText('second policy').closest('a')).toHaveAttribute( - 'href', - '/experimental/compliance/oval_policies/40' - ); - }); - it('should show empty state', async () => { - render(); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('No OVAL Policies found')).toBeInTheDocument(); - }); - it('should show errors', async () => { - render(); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect( - screen.getByText('Something very bad happened.') - ).toBeInTheDocument(); - expect(screen.getByText('Error!')).toBeInTheDocument(); - }); - it('should load page for user with permissions', async () => { - render(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('first policy')).toBeInTheDocument(); - }); - it('should not load page for user without permissions', async () => { - render(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.queryByText('first policy')).not.toBeInTheDocument(); - expect( - screen.getByText( - 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_policies.' - ) - ).toBeInTheDocument(); - expect(screen.getByText('Permission denied')).toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js b/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js deleted file mode 100644 index 4fae597c0..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesIndex/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; -import { showToast } from '../../../helpers/toastHelper'; - -import OvalPoliciesIndex from './OvalPoliciesIndex'; - -const WrappedOvalPoliciesIndex = props => { - const dispatch = useDispatch(); - - return ; -}; - -export default WrappedOvalPoliciesIndex; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js deleted file mode 100644 index 890046ac7..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/HostgroupSelect.js +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { useLazyQuery } from '@apollo/client'; -import { translate as __, sprintf } from 'foremanReact/common/I18n'; -import { - Select, - SelectOption, - SelectVariant, - FormGroup, -} from '@patternfly/react-core'; -import { ExclamationCircleIcon } from '@patternfly/react-icons'; -import hostgroupsQuery from '../../../graphql/queries/hostgroups.gql'; - -const HostgroupSelect = ({ - selected, - setSelected, - hgsError, - showError, - setShowError, -}) => { - const [isOpen, setIsOpen] = useState(false); - - const [typingTimeout, setTypingTimeout] = useState(null); - - const [fetchHostgroups, { loading, data, error }] = useLazyQuery( - hostgroupsQuery - ); - const results = data?.hostgroups?.nodes ? data.hostgroups.nodes : []; - - const onSelect = (event, selection) => { - if (selected.find(item => item.name === selection)) { - setSelected(selected.filter(item => item.name !== selection)); - } else { - const hg = results.find(item => item.name === selection); - setSelected([...selected, hg]); - } - }; - - const onClear = () => { - if (showError) { - setShowError(false); - } - setSelected([]); - }; - - const onInputChange = value => { - if (showError) { - setShowError(false); - } - if (typingTimeout) { - clearTimeout(typingTimeout); - } - const variables = { search: `name ~ ${value}` }; - setTypingTimeout(setTimeout(() => fetchHostgroups({ variables }), 500)); - }; - - const shouldValidate = (err, shouldShowError) => { - if (shouldShowError) { - return err ? 'error' : 'success'; - } - return 'noval'; - }; - - const prepareOptions = fetchedResults => { - if (loading) { - return [ - - {__('Loading...')} - , - ]; - } - - if (error) { - return [ - - {sprintf('Failed to fetch hostgroups, cause: %s', error.message)} - , - ]; - } - - if (fetchedResults.length > 20) { - return [ - - {sprintf( - 'You have %s hostgroups to display. Please refine your search.', - fetchedResults.length - )} - , - ]; - } - - return fetchedResults.map((hg, idx) => ( - - )); - }; - - return ( - } - helperTextInvalid={showError && hgsError} - validated={shouldValidate(hgsError, showError)} - > - - - ); -}; - -HostgroupSelect.propTypes = { - selected: PropTypes.array, - setSelected: PropTypes.func.isRequired, - hgsError: PropTypes.string, - showError: PropTypes.bool.isRequired, - setShowError: PropTypes.func.isRequired, -}; - -HostgroupSelect.defaultProps = { - selected: [], - hgsError: '', -}; - -export default HostgroupSelect; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js deleted file mode 100644 index ca7ef550a..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyForm.js +++ /dev/null @@ -1,119 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { Formik, Field as FormikField } from 'formik'; -import { useMutation } from '@apollo/client'; -import { translate as __ } from 'foremanReact/common/I18n'; -import { Button, Form as PfForm, ActionGroup } from '@patternfly/react-core'; - -import createOvalPolicy from '../../../graphql/mutations/createOvalPolicy.gql'; - -import { - TextField, - TextAreaField, - SelectField, -} from '../../../helpers/formFieldsHelper'; -import HostgroupSelect from './HostgroupSelect'; -import withLoading from '../../../components/withLoading'; - -import { ovalPoliciesPath } from '../../../helpers/pathsHelper'; -import LinkButton from '../../../components/LinkButton'; - -import { - createValidationSchema, - onSubmit, - initialValues, -} from './NewOvalPolicyFormHelpers'; - -const NewOvalPolicyForm = ({ history, showToast, ovalContents }) => { - const [callMutation] = useMutation(createOvalPolicy); - - const [assignedHgs, setAssignedHgs] = useState([]); - const [hgsShowError, setHgsShowError] = useState(false); - const [hgsError, setHgsError] = useState(''); - - const onHgsError = error => { - setHgsShowError(true); - setHgsError(error); - }; - - return ( - - {formProps => ( - - - - - - - - - - - - )} - - ); -}; - -NewOvalPolicyForm.propTypes = { - history: PropTypes.object.isRequired, - showToast: PropTypes.func.isRequired, - ovalContents: PropTypes.array.isRequired, -}; - -export default withLoading(NewOvalPolicyForm); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js deleted file mode 100644 index 40f30105d..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/NewOvalPolicyFormHelpers.js +++ /dev/null @@ -1,107 +0,0 @@ -import * as Yup from 'yup'; -import { translate as __, sprintf } from 'foremanReact/common/I18n'; - -import { ovalPoliciesPath } from '../../../helpers/pathsHelper'; -import { decodeId, decodeModelId } from '../../../helpers/globalIdHelper'; - -export const createValidationSchema = () => { - const cantBeBlank = __("can't be blank"); - - return Yup.object().shape({ - name: Yup.string().required(cantBeBlank), - ovalContentId: Yup.string().required(cantBeBlank), - cronLine: Yup.string().test( - 'is-cron', - __('is not a valid cronline'), - value => value && value.trim().split(' ').length === 5 - ), - }); -}; - -const partitionById = (array, name) => { - const res = array.reduce( - (memo, item) => { - if (item.id === name) { - memo.left.push(item); - } else { - memo.right.push(item); - } - return memo; - }, - { left: [], right: [] } - ); - return [res.left, res.right]; -}; - -const checksToMessage = checks => - checks.reduce((memo, check) => [...memo, check.failMsg], []).join(' '); - -export const onSubmit = ( - history, - showToast, - callMutation, - assignedHgs, - setHgsError -) => (values, actions) => { - const onCompleted = response => { - const failedChecks = response.data.createOvalPolicy.checkCollection.filter( - check => check.result === 'fail' - ); - if (failedChecks.length === 0) { - history.push(ovalPoliciesPath); - showToast({ - type: 'success', - message: __('OVAL Policy succesfully created.'), - }); - } else { - actions.setSubmitting(false); - - const [validationChecks, withoutValidationChecks] = partitionById( - failedChecks, - 'oval_policy_errors' - ); - - const [hgChecks, remainingChecks] = partitionById( - withoutValidationChecks, - 'hostgroups_without_proxy' - ); - if (validationChecks.length === 1) { - actions.setErrors(validationChecks[0].errors); - } - if (hgChecks.length > 0) { - setHgsError(checksToMessage(hgChecks)); - } - if (remainingChecks.length > 0) { - showToast({ - type: 'error', - message: checksToMessage(remainingChecks), - }); - } - } - }; - - const onError = response => { - showToast({ - type: 'error', - message: sprintf(__('Failed to create OVAL Policy: %s'), response.error), - }); - actions.setSubmitting(false); - }; - - const hostgroupIds = assignedHgs.map(decodeModelId); - const variables = { - ...values, - ovalContentId: decodeId(values.ovalContentId), - period: 'custom', - hostgroupIds, - }; - // eslint-disable-next-line promise/prefer-await-to-then - callMutation({ variables }).then(onCompleted, onError); -}; - -export const initialValues = { - name: '', - description: '', - ovalContentId: '', - cronLine: '', -}; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js deleted file mode 100644 index a8203138f..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/OvalPoliciesNew.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; -import { useQuery } from '@apollo/client'; -import { translate as __ } from 'foremanReact/common/I18n'; -import IndexLayout from '../../../components/IndexLayout'; - -import ovalContentsQuery from '../../../graphql/queries/ovalContents.gql'; -import NewOvalPolicyForm from './NewOvalPolicyForm'; - -const OvalPoliciesNew = props => { - const useFetchFn = () => useQuery(ovalContentsQuery); - - const renameData = data => ({ - ovalContents: data.ovalContents.nodes, - }); - - return ( - - - - ); -}; - -export default OvalPoliciesNew; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js deleted file mode 100644 index e14eea097..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.fixtures.js +++ /dev/null @@ -1,147 +0,0 @@ -import createOvalPolicy from '../../../../graphql/mutations/createOvalPolicy.gql'; -import hostgroupsQuery from '../../../../graphql/queries/hostgroups.gql'; - -import { mockFactory, admin } from '../../../../testHelper'; -import { decodeId } from '../../../../helpers/globalIdHelper'; -import { ovalContents } from '../../../OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures'; - -export const newPolicyName = 'test policy'; -export const newPolicyDescription = 'random description'; -export const newPolicyCronline = '5 5 5 5 5'; -export const newPolicyContentName = ovalContents.nodes[1].name; -export const newPolicyContentId = ovalContents.nodes[1].id; -const hostgroupId = 3; - -const createPolicyMockFactory = mockFactory( - 'createOvalPolicy', - createOvalPolicy -); -const hostgroupsMockFactory = mockFactory('hostgroups', hostgroupsQuery); - -const foremanAnsiblePresent = { - id: 'foreman_ansible_present', - errors: null, - failMsg: null, - result: 'pass', -}; -const rolePresent = { - id: 'foreman_scap_client_role_present', - errors: null, - failMsg: null, - result: 'pass', -}; -const roleVarsPresent = { - id: 'foreman_scap_client_vars_present', - errors: null, - failMsg: null, - result: 'pass', -}; -const serverVarOverriden = { - id: 'foreman_scap_client_server_overriden', - errors: null, - failMsg: null, - result: 'pass', -}; -const portVarOverriden = { - id: 'foreman_scap_client_port_overriden', - errors: null, - failMsg: null, - result: 'pass', -}; -const policiesVarOverriden = { - id: 'foreman_scap_client_policies_overriden', - errors: null, - failMsg: null, - result: 'pass', -}; -const policyErrors = { - id: 'oval_policy_errors', - errors: { name: 'has already been taken' }, - failMsg: null, - result: 'fail', -}; -export const hgWithoutProxy = { - id: 'hostgroups_without_proxy', - errors: null, - failMsg: 'Assign openscap_proxy to first hostgroup before proceeding.', - result: 'fail', -}; -export const roleAbsent = { - id: 'foreman_scap_client_role_present', - errors: null, - failMsg: - 'theforeman.foreman_scap_client Ansible Role not found, please import it before running this action again.', - result: 'fail', -}; - -const varChecks = [ - roleVarsPresent, - serverVarOverriden, - portVarOverriden, - policiesVarOverriden, -]; -const checkCollectionPass = [foremanAnsiblePresent, rolePresent, ...varChecks]; -const checkCollectionPreconditionFail = [ - foremanAnsiblePresent, - roleAbsent, - ...varChecks.map(check => ({ ...check, result: 'skip' })), -]; -const ovalPolicy = { - name: newPolicyName, - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTcw', - period: 'custom', - cronLine: newPolicyCronline, - hostgroups: { - nodes: [], - }, -}; - -const policyCreateSuccess = { - checkCollection: checkCollectionPass, - ovalPolicy, -}; - -const baseVariables = { - name: newPolicyName, - description: '', - ovalContentId: decodeId(newPolicyContentId), - cronLine: newPolicyCronline, - hostgroupIds: [], - period: 'custom', -}; - -export const firstHg = { - id: 'MDE6SG9zdGdyb3VwLTM=', - name: 'first hostgroup', -}; - -const successVariables = { - ...baseVariables, - description: newPolicyDescription, -}; - -export const policySuccessMock = createPolicyMockFactory( - successVariables, - policyCreateSuccess -); - -export const policyValidationMock = createPolicyMockFactory(baseVariables, { - checkCollection: [...checkCollectionPass, policyErrors], - ovalPolicy, -}); - -export const policyPreconditionMock = createPolicyMockFactory(baseVariables, { - checkCollection: checkCollectionPreconditionFail, - ovalPolicy, -}); - -export const policyInvalidHgMock = createPolicyMockFactory( - { ...baseVariables, hostgroupIds: [hostgroupId] }, - { checkCollection: [...checkCollectionPass, hgWithoutProxy], ovalPolicy } -); - -export const hostgroupsMock = hostgroupsMockFactory( - { search: `name ~ first` }, - { totalCount: 2, nodes: [firstHg] }, - { currentUser: admin } -); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js deleted file mode 100644 index c20aa968d..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/__tests__/OvalPoliciesNew.test.js +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; - -import OvalPoliciesNew from '../'; -import { ovalPoliciesPath } from '../../../../helpers/pathsHelper'; - -import { unpagedMocks as ovalContentMocks } from '../../../OvalContents/OvalContentsIndex/__tests__/OvalContentsIndex.fixtures'; - -import { - withMockedProvider, - wait, - withRouter, - withRedux, -} from '../../../../testHelper'; - -import { - newPolicyName, - newPolicyDescription, - newPolicyCronline, - newPolicyContentName, - policySuccessMock, - policyValidationMock, - policyPreconditionMock, - policyInvalidHgMock, - hostgroupsMock, - firstHg, - roleAbsent as roleAbsentCheck, - hgWithoutProxy as withoutProxyCheck, -} from './OvalPoliciesNew.fixtures'; - -import * as toasts from '../../../../helpers/toastHelper'; - -const TestComponent = withRouter( - withRedux(withMockedProvider(OvalPoliciesNew)) -); - -describe('OvalPoliciesNew', () => { - it('should create new OVAL policy', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - const pushMock = jest.fn(); - - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await wait(); - const submitBtn = screen.getByRole('button', { name: 'submit' }); - expect(submitBtn).toBeDisabled(); - userEvent.type(screen.getByLabelText(/name/), newPolicyName); - await wait(); - expect(submitBtn).toBeDisabled(); - userEvent.type(screen.getByLabelText(/cronLine/), 'foo'); - userEvent.type(screen.getByLabelText(/description/), newPolicyDescription); - userEvent.selectOptions( - screen.getByLabelText(/ovalContentId/), - newPolicyContentName - ); - await wait(); - expect(screen.getByText('is not a valid cronline')).toBeInTheDocument(); - expect(submitBtn).toBeDisabled(); - userEvent.clear(screen.getByLabelText(/cronLine/)); - userEvent.type(screen.getByLabelText(/cronLine/), newPolicyCronline); - await wait(); - expect( - screen.queryByText('is not a valid cronline') - ).not.toBeInTheDocument(); - expect(submitBtn).not.toBeDisabled(); - userEvent.click(submitBtn); - await wait(2); - expect(pushMock).toHaveBeenCalledWith(ovalPoliciesPath); - expect(showToast).toHaveBeenCalledWith({ - type: 'success', - message: 'OVAL Policy succesfully created.', - }); - }); - it('should not create new policy on validation error', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - const pushMock = jest.fn(); - - render( - - ); - await wait(); - userEvent.type(screen.getByLabelText(/name/), newPolicyName); - userEvent.type(screen.getByLabelText(/cronLine/), newPolicyCronline); - userEvent.selectOptions( - screen.getByLabelText(/ovalContentId/), - newPolicyContentName - ); - await wait(); - userEvent.click(screen.getByRole('button', { name: 'submit' })); - await wait(2); - expect(pushMock).not.toHaveBeenCalled(); - expect(showToast).not.toHaveBeenCalled(); - expect(screen.getByText('has already been taken')).toBeInTheDocument(); - }); - it('should not create policy on preconditions error', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - const pushMock = jest.fn(); - - render( - - ); - await wait(); - userEvent.type(screen.getByLabelText(/name/), newPolicyName); - userEvent.type(screen.getByLabelText(/cronLine/), newPolicyCronline); - userEvent.selectOptions( - screen.getByLabelText(/ovalContentId/), - newPolicyContentName - ); - await wait(); - userEvent.click(screen.getByRole('button', { name: 'submit' })); - await wait(2); - await wait(); - expect(pushMock).not.toHaveBeenCalled(); - expect(showToast).toHaveBeenCalledWith({ - type: 'error', - message: roleAbsentCheck.failMsg, - }); - }); - it('should show hostgroup errros', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - const pushMock = jest.fn(); - - render( - - ); - await wait(); - userEvent.type(screen.getByLabelText(/name/), newPolicyName); - userEvent.type(screen.getByLabelText(/cronLine/), newPolicyCronline); - userEvent.selectOptions( - screen.getByLabelText(/ovalContentId/), - newPolicyContentName - ); - userEvent.type(screen.getByLabelText(/hostgroup/), 'first'); - await wait(500); - userEvent.click(screen.getByText(firstHg.name)); - await wait(); - userEvent.click(screen.getByRole('button', { name: 'submit' })); - await wait(2); - expect(pushMock).not.toHaveBeenCalled(); - expect(screen.getByText(withoutProxyCheck.failMsg)).toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js b/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js deleted file mode 100644 index 6347e1e32..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesNew/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { useDispatch } from 'react-redux'; - -import { showToast } from '../../../helpers/toastHelper'; -import OvalPoliciesNew from './OvalPoliciesNew'; - -const WrappedOvalPoliciesNew = props => ( - -); - -export default WrappedOvalPoliciesNew; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js deleted file mode 100644 index 3e93c9484..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTab.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import { useQuery } from '@apollo/client'; - -import CvesTable from './CvesTable'; - -import cves from '../../../graphql/queries/cves.gql'; -import { - useParamsToVars, - useCurrentPagination, -} from '../../../helpers/pageParamsHelper'; - -const CvesTab = props => { - const useFetchFn = componentProps => - useQuery(cves, { - variables: { - search: `oval_policy_id = ${componentProps.match.params.id}`, - ...useParamsToVars(componentProps.history), - }, - }); - - const renameData = data => ({ - cves: data.cves.nodes, - totalCount: data.cves.totalCount, - }); - - const pagination = useCurrentPagination(props.history); - - return ( - - ); -}; - -CvesTab.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - -export default CvesTab; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js deleted file mode 100644 index b49026818..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/CvesTable.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import { linkCell } from '../../../helpers/tableHelper'; -import { hostsPath } from '../../../helpers/pathsHelper'; -import { decodeModelId } from '../../../helpers/globalIdHelper'; -import { addSearch } from '../../../helpers/pageParamsHelper'; - -import withLoading from '../../../components/withLoading'; -import IndexTable from '../../../components/IndexTable'; - -const CvesTable = props => { - const columns = [ - { title: __('Ref Id') }, - { title: __('Has Errata?') }, - { title: __('Hosts Count') }, - ]; - - const cveRefId = cve => ( - - {cve.refId} - - ); - - const hostCount = cve => - linkCell( - addSearch(hostsPath, { search: `cve_id = ${decodeModelId(cve)}` }), - cve.hosts.nodes.length - ); - - const rows = props.cves.map(cve => ({ - cells: [ - { title: cveRefId(cve) }, - { title: cve.hasErrata ? __('Yes') : __('No') }, - { title: hostCount(cve) }, - ], - cve, - })); - - const actions = []; - - return ( - - ); -}; - -CvesTable.propTypes = { - cves: PropTypes.array.isRequired, - pagination: PropTypes.object.isRequired, - totalCount: PropTypes.number.isRequired, - history: PropTypes.object.isRequired, -}; - -export default withLoading(CvesTable); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js deleted file mode 100644 index bbbd3d0f8..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/DetailsTab.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useMutation } from '@apollo/client'; - -import { - TextList, - TextContent, - TextArea, - TextListItem, - TextListVariants, - TextListItemVariants, - TextInput, -} from '@patternfly/react-core'; - -import { translate as __ } from 'foremanReact/common/I18n'; - -import EditableInput from '../../../components/EditableInput'; - -import { onAttrUpdate, policySchedule } from './OvalPoliciesShowHelper'; -import updateOvalPolicyMutation from '../../../graphql/mutations/updateOvalPolicy.gql'; - -const DetailsTab = props => { - const { policy, showToast } = props; - - const [callMutation] = useMutation(updateOvalPolicyMutation); - - return ( - - - - {__('Name')} - - - - - - {__('Period')} - - - {policySchedule(policy)} - - - {__('Description')} - - - - - - - ); -}; - -DetailsTab.propTypes = { - policy: PropTypes.object.isRequired, - showToast: PropTypes.func.isRequired, -}; - -export default DetailsTab; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js deleted file mode 100644 index 879184265..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTab.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import { useQuery } from '@apollo/client'; - -import HostgroupsTable from './HostgroupsTable'; - -import hostgroups from '../../../graphql/queries/hostgroups.gql'; -import { - useParamsToVars, - useCurrentPagination, -} from '../../../helpers/pageParamsHelper'; - -const HostgroupsTab = props => { - const useFetchFn = componentProps => - useQuery(hostgroups, { - variables: { - search: `oval_policy_id = ${componentProps.match.params.id}`, - ...useParamsToVars(componentProps.history), - }, - }); - - const renameData = data => ({ - hostgroups: data.hostgroups.nodes, - totalCount: data.hostgroups.totalCount, - }); - - const pagination = useCurrentPagination(props.history); - - return ( - - ); -}; - -HostgroupsTab.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - -export default HostgroupsTab; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js deleted file mode 100644 index 419c99b19..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/HostgroupsTable.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { translate as __ } from 'foremanReact/common/I18n'; - -import withLoading from '../../../components/withLoading'; -import IndexTable from '../../../components/IndexTable'; - -const CvesTable = props => { - const columns = [{ title: __('Name') }]; - - const rows = props.hostgroups.map(hostgroup => ({ - cells: [{ title: hostgroup.name }], - hostgroup, - })); - - const actions = []; - - return ( - - ); -}; - -CvesTable.propTypes = { - hostgroups: PropTypes.array.isRequired, - pagination: PropTypes.object.isRequired, - totalCount: PropTypes.number.isRequired, - history: PropTypes.object.isRequired, -}; - -export default withLoading(CvesTable); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js deleted file mode 100644 index c87d35fe1..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShow.js +++ /dev/null @@ -1,82 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; -import { Helmet } from 'react-helmet'; -import { translate as __ } from 'foremanReact/common/I18n'; -import { - Button, - Grid, - GridItem, - Text, - TextVariants, - Tabs, - Tab, - TabTitleText, -} from '@patternfly/react-core'; - -import withLoading from '../../../components/withLoading'; -import CvesTab from './CvesTab'; -import HostgroupsTab from './HostgroupsTab'; -import DetailsTab from './DetailsTab'; - -import { newJobFormPath } from './OvalPoliciesShowHelper'; -import { resolvePath } from '../../../helpers/pathsHelper'; - -const OvalPoliciesShow = props => { - const { policy, match, history } = props; - const activeTab = match.params.tab ? match.params.tab : 'details'; - - const handleTabSelect = (event, value) => { - history.push( - resolvePath(match.path, { ':id': match.params.id, ':tab?': value }) - ); - }; - - return ( - - - {`${policy.name} | OVAL Policy`} - - - - {policy.name} - - - - - - - - - {__('Details')}} - > - - - {__('CVEs')}} - > - - - {__('Hostgroups')}} - > - - - - - - - ); -}; - -OvalPoliciesShow.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - policy: PropTypes.object.isRequired, -}; - -export default withLoading(OvalPoliciesShow); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js deleted file mode 100644 index 2b3c90d23..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/OvalPoliciesShowHelper.js +++ /dev/null @@ -1,117 +0,0 @@ -import { translate as __, sprintf } from 'foremanReact/common/I18n'; -import { decodeModelId } from '../../../helpers/globalIdHelper'; -import { addSearch } from '../../../helpers/pageParamsHelper'; -import { newJobPath } from '../../../helpers/pathsHelper'; - -export const policySchedule = policy => { - switch (policy.period) { - case 'weekly': - return sprintf(__('Weekly, on %s'), policy.weekday); - case 'monthly': - return sprintf(__('Monthly, day of month: %s'), policy.dayOfMonth); - case 'custom': - return sprintf(__('Custom cron: %s'), policy.cronLine); - default: - return __('Unknown schedule'); - } -}; - -const targetingScopedSearchQuery = policy => { - const hgIds = policy.hostgroups.nodes.reduce((memo, hg) => { - const ids = [decodeModelId(hg)].concat( - hg.descendants.nodes.map(decodeModelId) - ); - return ids.reduce( - (acc, id) => (acc.includes(id) ? acc : [...acc, id]), - memo - ); - }, []); - - if (hgIds.length === 0) { - return ''; - } - - return `hostgroup_id ^ (${hgIds.join(' ')})`; -}; - -export const newJobFormPath = (policy, policyId) => - addSearch(newJobPath, { - feature: 'foreman_openscap_run_oval_scans', - host_ids: targetingScopedSearchQuery(policy), - 'inputs[oval_policies]': policyId, - }); - -const policyToAttrs = (policy, attrs) => - Object.entries(policy).reduce((memo, [key, value]) => { - if (attrs.includes(key)) { - memo[key] = value; - } - return memo; - }, {}); - -const onUpdateSuccess = ( - closeEditable, - stopSubmitting, - showToast, - attr, - onValidationError -) => result => { - const { errors } = result.data.updateOvalPolicy; - if (Array.isArray(errors) && errors.length > 0) { - stopSubmitting(); - if ( - errors.length === 1 && - errors[0].path.join(' ') === `attributes ${attr}` - ) { - onValidationError(errors[0].message); - } else { - showToast({ - type: 'error', - message: formatError(joinErrors(errors)), - }); - } - } else { - closeEditable(); - showToast({ - type: 'success', - message: __('OVAL policy was successfully updated.'), - }); - } -}; - -const formatError = error => - sprintf( - __('There was a following error when updating OVAL policy: %s'), - error - ); - -const joinErrors = errors => errors.map(err => err.message).join(', '); - -const onUpdateError = (showToast, stopSubmitting) => error => { - stopSubmitting(); - showToast({ type: 'error', message: formatError(error.message) }); -}; - -export const onAttrUpdate = (attr, policy, callMutation, showToast) => ( - newValue, - closeEditable, - stopSubmitting, - onValidationError -) => { - const vars = policyToAttrs(policy, ['id', 'name', 'description', 'cronLine']); - vars[attr] = newValue; - return ( - callMutation({ variables: vars }) - // eslint-disable-next-line promise/prefer-await-to-then - .then( - onUpdateSuccess( - closeEditable, - stopSubmitting, - showToast, - attr, - onValidationError - ) - ) - .catch(onUpdateError(showToast, stopSubmitting)) - ); -}; diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js deleted file mode 100644 index 2d82c2d68..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.fixtures.js +++ /dev/null @@ -1,48 +0,0 @@ -import { mockFactory } from '../../../../testHelper'; -import updateOvalPolicyMutation from '../../../../graphql/mutations/updateOvalPolicy.gql'; -import { ovalPolicy } from './OvalPoliciesShow.fixtures'; - -const updateOvalPolicyMockFactory = mockFactory( - 'updateOvalPolicy', - updateOvalPolicyMutation -); - -export const updatedName = 'updated policy name'; - -const variables = { - id: ovalPolicy.id, - name: updatedName, - cronLine: ovalPolicy.cronLine, - description: ovalPolicy.description, -}; -const responsePolicy = { - ovalPolicy: { - __typename: 'ForemanOpenscap::OvalPolicy', - id: ovalPolicy.id, - name: updatedName, - description: ovalPolicy.description, - cronLine: ovalPolicy.cronLine, - }, - errors: [], -}; - -export const policyUpdateMock = updateOvalPolicyMockFactory( - variables, - responsePolicy -); - -export const policyUpdateErrorMock = updateOvalPolicyMockFactory( - variables, - responsePolicy, - { errors: [{ message: 'This is an unexpected failure.' }] } -); - -export const policyUpdateValidationMock = updateOvalPolicyMockFactory( - variables, - { - ovalPolicy, - errors: [ - { path: ['attributes', 'name'], message: 'has already been taken' }, - ], - } -); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js deleted file mode 100644 index a881bb47b..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesEdit.test.js +++ /dev/null @@ -1,202 +0,0 @@ -import React from 'react'; - -import { render, screen, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; - -import OvalPoliciesShow from '../'; -import { - historyMock, - ovalPolicyId, - policyDetailMock, - policyEditPermissionsMock, - ovalPolicy, -} from './OvalPoliciesShow.fixtures'; -import { - policyUpdateMock, - policyUpdateErrorMock, - policyUpdateValidationMock, - updatedName, -} from './OvalPoliciesEdit.fixtures'; -import { ovalPoliciesShowPath } from '../../../../helpers/pathsHelper'; - -import { - withMockedProvider, - tick, - withRouter, - withRedux, -} from '../../../../testHelper'; - -import * as toasts from '../../../../helpers/toastHelper'; - -const TestComponent = withRouter( - withRedux(withMockedProvider(OvalPoliciesShow)) -); - -describe('OvalPoliciesShow', () => { - it('should open and close inline edit for name', async () => { - render( - - ); - await waitFor(tick); - userEvent.click(screen.getByRole('button', { name: 'edit name' })); - userEvent.clear(screen.getByLabelText(/name text input/)); - userEvent.type(screen.getByLabelText(/name text input/), 'foo'); - expect(screen.getByLabelText(/name text input/)).toHaveAttribute( - 'value', - 'foo' - ); - userEvent.click( - screen.getByRole('button', { name: 'cancel editing name' }) - ); - expect(screen.queryByText('foo')).not.toBeInTheDocument(); - }); - it('should update policy name', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - - const { container } = render( - - ); - await waitFor(tick); - const editBtn = screen.getByRole('button', { name: 'edit name' }); - expect(editBtn).toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'submit name' }) - ).not.toBeInTheDocument(); - - userEvent.click(editBtn); - expect( - screen.queryByRole('button', { name: 'edit name' }) - ).not.toBeInTheDocument(); - const inputField = screen.getByLabelText(/name text input/); - const submitBtn = screen.getByRole('button', { name: 'submit name' }); - const cancelBtn = screen.getByRole('button', { - name: 'cancel editing name', - }); - - userEvent.clear(inputField); - userEvent.type(inputField, updatedName); - userEvent.click(submitBtn); - expect(inputField).toBeDisabled(); - expect(submitBtn).toBeDisabled(); - expect(cancelBtn).toBeDisabled(); - const spinner = container.querySelector('#edit-name-spinner'); - expect(spinner).toBeInTheDocument(); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'success', - message: 'OVAL policy was successfully updated.', - }); - - expect(inputField).not.toBeInTheDocument(); - expect(editBtn).toBeInTheDocument(); - expect(cancelBtn).not.toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'submit name' }) - ).not.toBeInTheDocument(); - await waitFor(tick); - expect(screen.getAllByText(updatedName).pop()).toBeInTheDocument(); - }); - it('should show unexpected errors', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - - render( - - ); - await waitFor(tick); - const editBtn = screen.getByRole('button', { name: 'edit name' }); - userEvent.click(editBtn); - const inputField = screen.getByLabelText(/name text input/); - userEvent.clear(inputField); - userEvent.type(inputField, updatedName); - userEvent.click(screen.getByRole('button', { name: 'submit name' })); - await waitFor(tick); - expect(showToast).toHaveBeenCalledWith({ - type: 'error', - message: - 'There was a following error when updating OVAL policy: This is an unexpected failure.', - }); - expect(inputField).toBeInTheDocument(); - expect(inputField).not.toBeDisabled(); - expect(screen.getByText(ovalPolicy.name)).toBeInTheDocument(); - }); - it('should show validation errors', async () => { - const showToast = jest.fn(); - jest.spyOn(toasts, 'showToast').mockImplementation(() => showToast); - - const { container } = render( - - ); - await waitFor(tick); - const editBtn = screen.getByRole('button', { name: 'edit name' }); - userEvent.click(editBtn); - const inputField = screen.getByLabelText(/name text input/); - userEvent.clear(inputField); - userEvent.type(inputField, updatedName); - userEvent.click(screen.getByRole('button', { name: 'submit name' })); - await waitFor(tick); - expect(inputField).toBeInTheDocument(); - expect(inputField).not.toBeDisabled(); - expect( - container.querySelector('#edit-name-spinner') - ).not.toBeInTheDocument(); - expect(screen.getByText(ovalPolicy.name)).toBeInTheDocument(); - expect(screen.getByText('has already been taken')).toBeInTheDocument(); - userEvent.click( - screen.getByRole('button', { name: 'cancel editing name' }) - ); - userEvent.click(editBtn); - expect( - screen.queryByText('has already been taken') - ).not.toBeInTheDocument(); - }); - it('should not show edit btns when user is not allowed to edit', async () => { - render( - - ); - await waitFor(tick); - expect( - screen.queryByRole('button', { name: 'edit name' }) - ).not.toBeInTheDocument(); - expect( - screen.queryByRole('button', { name: 'edit description' }) - ).not.toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js deleted file mode 100644 index a0730bf64..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.fixtures.js +++ /dev/null @@ -1,124 +0,0 @@ -import { mockFactory, admin, intruder, viewer } from '../../../../testHelper'; -import ovalPolicyQuery from '../../../../graphql/queries/ovalPolicy.gql'; -import cvesQuery from '../../../../graphql/queries/cves.gql'; -import hostgroupsQuery from '../../../../graphql/queries/hostgroups.gql'; - -const policyDetailMockFactory = mockFactory('ovalPolicy', ovalPolicyQuery); -const cvesMockFactory = mockFactory('cves', cvesQuery); -const hostgroupsMockFactory = mockFactory('hostgroups', hostgroupsQuery); - -export const ovalPolicy = { - __typename: 'ForemanOpenscap::OvalPolicy', - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpPdmFsUG9saWN5LTM=', - name: 'Third policy', - period: 'weekly', - cronLine: null, - weekday: 'tuesday', - dayOfMonth: null, - description: 'A very strict policy', - meta: { - canEdit: true, - }, - hostgroups: { - nodes: [ - { - id: 'MDE6SG9zdGdyb3VwLTQ=', - name: 'oval hg', - descendants: { - nodes: [ - { id: 'MDE6SG9zdGdyb3VwLTEw' }, - { id: 'MDE6SG9zdGdyb3VwLTEy' }, - { id: 'MDE6SG9zdGdyb3VwLTEx' }, - ], - }, - }, - ], - }, -}; - -const noEditPolicy = { ...ovalPolicy, meta: { canEdit: false } }; - -const cvesResult = { - totalCount: 1, - nodes: [ - { - id: 'MDE6Rm9yZW1hbk9wZW5zY2FwOjpDdmUtMjY3', - refId: 'CVE-2020-14365', - refUrl: 'https://access.redhat.com/security/cve/CVE-2020-14365', - definitionId: 'oval:com.redhat.rhsa:def:20203601', - hasErrata: true, - hosts: { - nodes: [ - { - id: 'MDE6SG9zdC0z', - name: 'centos-random.example.com', - }, - ], - }, - }, - ], -}; - -const hostgroupsResult = { - totalCount: 2, - nodes: [ - { - id: 'MDE6SG9zdGdyb3VwLTQ=', - name: 'first hostgroup', - }, - { - id: 'MDE6SG9zdGdyb3VwLTEy', - name: 'second hostgroup', - }, - ], -}; - -export const ovalPolicyId = 3; - -export const pushMock = jest.fn(); - -export const historyMock = { - location: { - search: '', - }, - push: pushMock, -}; - -export const historyWithSearch = { - location: { - search: '?page=1&perPage=5', - }, -}; - -export const policyDetailMock = policyDetailMockFactory( - { id: ovalPolicy.id }, - ovalPolicy, - { currentUser: admin } -); - -export const policyUnauthorizedMock = policyDetailMockFactory( - { id: ovalPolicy.id }, - ovalPolicy, - { currentUser: intruder } -); - -export const policyCvesMock = cvesMockFactory( - { search: `oval_policy_id = ${ovalPolicyId}`, first: 5, last: 5 }, - cvesResult, - { currentUser: admin } -); -export const policyHostgroupsMock = hostgroupsMockFactory( - { search: `oval_policy_id = ${ovalPolicyId}`, first: 5, last: 5 }, - hostgroupsResult, - { currentUser: admin } -); -export const policyHostgroupsDeniedMock = hostgroupsMockFactory( - { search: `oval_policy_id = ${ovalPolicyId}`, first: 5, last: 5 }, - { totalCount: 0, nodes: [] }, - { currentUser: intruder } -); -export const policyEditPermissionsMock = policyDetailMockFactory( - { id: ovalPolicy.id }, - noEditPolicy, - { currentUser: viewer } -); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js deleted file mode 100644 index 5ed2c86e0..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/__tests__/OvalPoliciesShow.test.js +++ /dev/null @@ -1,172 +0,0 @@ -import React from 'react'; -import { Router } from 'react-router-dom'; -import { render, screen, waitFor } from '@testing-library/react'; -import { within } from '@testing-library/dom'; -import '@testing-library/jest-dom'; -import userEvent from '@testing-library/user-event'; -import { createMemoryHistory } from 'history'; - -import OvalPoliciesShow from '../index'; -import { - ovalPoliciesShowPath, - resolvePath, -} from '../../../../helpers/pathsHelper'; - -import { - withRedux, - withMockedProvider, - tick, - withRouter, -} from '../../../../testHelper'; -import { - policyDetailMock, - historyMock, - historyWithSearch, - pushMock, - policyCvesMock, - policyHostgroupsMock, - policyHostgroupsDeniedMock, - ovalPolicyId, - policyUnauthorizedMock, -} from './OvalPoliciesShow.fixtures'; - -const TestComponent = withRedux( - withRouter(withMockedProvider(OvalPoliciesShow)) -); - -describe('OvalPoliciesShow', () => { - it('should load details by default and handle tab change', async () => { - const { container } = render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getAllByText('Third policy').pop()).toBeInTheDocument(); - expect(screen.getByText('Weekly, on tuesday')).toBeInTheDocument(); - expect(screen.getByText('A very strict policy')).toBeInTheDocument(); - const activeTabHeader = container.querySelector( - '.pf-c-tabs__item.pf-m-current' - ); - expect(within(activeTabHeader).getByText('Details')).toBeInTheDocument(); - userEvent.click(screen.getByRole('tab', { name: 'CVEs' })); - expect(pushMock).toHaveBeenCalledWith( - resolvePath(ovalPoliciesShowPath, { - ':id': ovalPolicyId, - ':tab?': 'cves', - }) - ); - }); - it('should load details tab when specified in URL', async () => { - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('Weekly, on tuesday')).toBeInTheDocument(); - }); - it('should not load the page when user does not have permissions', async () => { - render( - - ); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect( - screen.getByText( - 'You are not authorized to view the page. Request the following permissions from administrator: view_oval_policies.' - ) - ).toBeInTheDocument(); - }); - it('should load CVEs tab when specified in URL', async () => { - const mocks = policyDetailMock.concat(policyCvesMock); - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('CVE-2020-14365')).toBeInTheDocument(); - }); - it('should have button for scanning all hostgroups', async () => { - const btnText = 'Scan All Hostgroups'; - - const WithProvider = withRedux(withMockedProvider(OvalPoliciesShow)); - const history = createMemoryHistory(); - history.push = jest.fn(); - - render( - - - - ); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText(btnText)).toBeInTheDocument(); - userEvent.click(screen.getByRole('button', { name: btnText })); - expect(history.push).toHaveBeenCalledWith( - '/job_invocations/new?feature=foreman_openscap_run_oval_scans&host_ids=hostgroup_id+%5E+%284+10+12+11%29&inputs%5Boval_policies%5D=3' - ); - }); - it('should load hostgroups tab when specified in URL', async () => { - const mocks = policyDetailMock.concat(policyHostgroupsMock); - render( - - ); - expect(screen.getByText('Loading')).toBeInTheDocument(); - await waitFor(tick); - await waitFor(tick); - expect(screen.queryByText('Loading')).not.toBeInTheDocument(); - expect(screen.getByText('first hostgroup')).toBeInTheDocument(); - }); - it('should not show hostgroups for a user without permissions', async () => { - const mocks = policyDetailMock.concat(policyHostgroupsDeniedMock); - render( - - ); - await waitFor(tick); - await waitFor(tick); - expect(screen.getByText('Permission denied')).toBeInTheDocument(); - }); -}); diff --git a/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js b/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js deleted file mode 100644 index 71cb5df7f..000000000 --- a/webpack/routes/OvalPolicies/OvalPoliciesShow/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { useQuery } from '@apollo/client'; -import { useDispatch } from 'react-redux'; - -import { translate as __ } from 'foremanReact/common/I18n'; -import { showToast } from '../../../helpers/toastHelper'; - -import OvalPoliciesShow from './OvalPoliciesShow'; -import { encodeId } from '../../../helpers/globalIdHelper'; - -import ovalPolicy from '../../../graphql/queries/ovalPolicy.gql'; - -const WrappedOvalPoliciesShow = props => { - const id = encodeId('ForemanOpenscap::OvalPolicy', props.match.params.id); - - const useFetchFn = componentProps => - useQuery(ovalPolicy, { variables: { id } }); - - const renameData = data => ({ policy: data.ovalPolicy }); - - return ( - - ); -}; - -WrappedOvalPoliciesShow.propTypes = { - match: PropTypes.object.isRequired, -}; - -export default WrappedOvalPoliciesShow; diff --git a/webpack/routes/routes.js b/webpack/routes/routes.js deleted file mode 100644 index a0866a524..000000000 --- a/webpack/routes/routes.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import OvalContentsIndex from './OvalContents/OvalContentsIndex'; -import OvalContentsShow from './OvalContents/OvalContentsShow'; -import OvalContentsNew from './OvalContents/OvalContentsNew'; -import OvalPoliciesIndex from './OvalPolicies/OvalPoliciesIndex'; -import OvalPoliciesNew from './OvalPolicies/OvalPoliciesNew'; -import OvalPoliciesShow from './OvalPolicies/OvalPoliciesShow'; - -import { - ovalContentsPath, - ovalContentsShowPath, - ovalContentsNewPath, - ovalPoliciesPath, - ovalPoliciesShowPath, - ovalPoliciesNewPath, -} from '../helpers/pathsHelper'; - -export default [ - { - path: ovalContentsPath, - render: props => , - exact: true, - }, - { - path: ovalContentsNewPath, - render: props => , - exact: true, - }, - { - path: ovalContentsShowPath, - render: props => , - exact: true, - }, - { - path: ovalPoliciesPath, - render: props => , - exact: true, - }, - { - path: ovalPoliciesNewPath, - render: props => , - exact: true, - }, - { - path: ovalPoliciesShowPath, - render: props => , - exact: true, - }, -];