Skip to content

Commit

Permalink
Fixes #37772 - Add controller guardrails for multi-CV params
Browse files Browse the repository at this point in the history
  also some light refactoring around candlepin names
  and content view environments
  • Loading branch information
jeremylenz committed Sep 13, 2024
1 parent 746bda7 commit ffbebf3
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 21 deletions.
9 changes: 7 additions & 2 deletions app/controllers/katello/api/v2/activation_keys_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Katello
class Api::V2::ActivationKeysController < Api::V2::ApiController # rubocop:disable Metrics/ClassLength
include Katello::Concerns::FilteredAutoCompleteSearch
include Katello::Concerns::Api::V2::ContentOverridesController
include Katello::Concerns::Api::V2::MultiCVParamsHandling
before_action :verify_presence_of_organization_or_environment, :only => [:index]
before_action :find_optional_organization, :only => [:index, :create, :show]
before_action :find_authorized_katello_resource, :only => [:show, :update, :destroy, :available_releases,
Expand Down Expand Up @@ -286,7 +287,7 @@ def find_cve_for_single
content_view_id: content_view_id).first
if cve.blank?
fail HttpErrors::NotFound, _("Couldn't find content view environment with content view ID '%{cv}'"\
" or environment ID '%{env}'") % { cv: content_view_id, env: environment_id }
" and environment ID '%{env}'") % { cv: content_view_id, env: environment_id }
end
@content_view_environments = [cve]
end
Expand All @@ -297,9 +298,13 @@ def find_content_view_environments
find_cve_for_single
elsif params[:content_view_environments] || params[:content_view_environment_ids]
@content_view_environments = ::Katello::ContentViewEnvironment.fetch_content_view_environments(
labels: params[:content_view_environments],
candlepin_names: params[:content_view_environments],
ids: params[:content_view_environment_ids],
organization: @organization || @activation_key&.organization)
if @content_view_environments.blank?
handle_errors(candlepin_names: params[:content_view_environments],
ids: params[:content_view_environment_ids])
end
end
@organization ||= @content_view_environments.first&.organization
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Concerns
module Api::V2::HostsControllerExtensions
extend ActiveSupport::Concern
include ForemanTasks::Triggers
include Katello::Concerns::Api::V2::MultiCVParamsHandling

module Overrides
def action_permission
Expand Down Expand Up @@ -46,11 +47,15 @@ def set_content_view_environments
return if content_facet_attributes.blank? || @host&.content_facet.blank? ||
(cve_params[:content_view_id].present? && cve_params[:lifecycle_environment_id].present?)
cves = ::Katello::ContentViewEnvironment.fetch_content_view_environments(
labels: cve_params[:content_view_environments],
candlepin_names: cve_params[:content_view_environments],
ids: cve_params[:content_view_environment_ids],
organization: @organization || @host&.organization)

@host.content_facet.content_view_environments = cves if cves.present?
if cves.present?
@host.content_facet.content_view_environments = cves
else
handle_errors(candlepin_names: cve_params[:content_view_environments],
ids: cve_params[:content_view_environment_ids])
end
end

def cve_params
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Katello
module Concerns
module Api::V2::MultiCVParamsHandling
extend ActiveSupport::Concern
include ::Katello::Api::V2::ErrorHandling

def handle_errors(candlepin_names: [], ids: [])
if candlepin_names.present?
fail HttpErrors::UnprocessableEntity, "No content view environments found with names: #{candlepin_names.join(',')}"
elsif ids.present?
fail HttpErrors::UnprocessableEntity, "No content view environments found with ids: #{ids}"
end
rescue HttpErrors::UnprocessableEntity => error
respond_for_exception(
error,
:status => :unprocessable_entity,
:text => error.message,
:errors => [error.message],
:with_logging => true
)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Actions
module Pulp3
module Repository
class UpdateCvRepositoryCertGuard < Pulp3::Abstract
class UpdateCVRepositoryCertGuard < Pulp3::Abstract
def plan(repository, smart_proxy)
root = repository.root
cv_repositories = root.repositories - [root.library_instance]
Expand Down
21 changes: 7 additions & 14 deletions app/models/katello/content_view_environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,7 @@ def self.for_content_facets(content_facets)
end

def self.with_candlepin_name(cp_name, organization: Organization.current)
lce_name, cv_name = cp_name.split('/')
if cv_name.blank? && lce_name == 'Library'
return default.find_by(
environment: ::Katello::KTEnvironment.library.where(organization: organization)&.first
)
end
joins(:environment, :content_view).where("#{Katello::KTEnvironment.table_name}.label" => lce_name, "#{Katello::ContentView.table_name}.label" => cv_name).first
joins(:environment, :content_view).where("#{Katello::KTEnvironment.table_name}.organization_id" => organization, label: cp_name).first
end

# retrieve the owning environment for this content view environment.
Expand All @@ -63,8 +57,7 @@ def default_environment?
end

def candlepin_name
return environment.label if default_environment?
"#{environment.label}/#{content_view.label}"
label
end

def priority(content_object)
Expand All @@ -75,16 +68,16 @@ def priority(content_object)
end
end

def self.fetch_content_view_environments(labels: [], ids: [], organization:)
def self.fetch_content_view_environments(candlepin_names: [], ids: [], organization:)
# Must do maps here to ensure CVEs remain in the same order.
# Using ActiveRecord .where will return them in a different order.
if ids.present?
ids.map! do |id|
cves = ids.map do |id|
::Katello::ContentViewEnvironment.find_by(id: id)
end
ids.compact
elsif labels.present?
environment_names = labels.map(&:strip)
cves.compact
elsif candlepin_names.present?
environment_names = candlepin_names.map(&:strip)
environment_names.map! do |name|
with_candlepin_name(name, organization: organization)
end
Expand Down
1 change: 1 addition & 0 deletions config/initializers/inflections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
inflect.singular 'bases', 'base'

inflect.acronym 'SCA' # Simple Content Access
inflect.acronym 'CV' # Content view
end
60 changes: 60 additions & 0 deletions test/controllers/api/v2/activation_keys_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,46 @@ def test_create_with_description
assert_equal key_description, response['description']
end

def test_create_with_content_view_environments_param
cve = katello_content_view_environments(:library_dev_view_library)
cve.update(organization: @organization)
content_view_environments = ['published_dev_view_library']
ActivationKey.any_instance.expects(:reload)
assert_sync_task(::Actions::Katello::ActivationKey::Create) do |activation_key|
assert_equal content_view_environments, activation_key.content_view_environments.map(&:candlepin_name), [cve.candlepin_name]
assert_valid activation_key
end

post :create, params: {
:organization_id => @organization.id,
:content_view_environments => content_view_environments,
:activation_key => {:name => 'new key'}
}

assert_response :success
assert_template 'katello/api/v2/common/create'
end

def test_create_with_content_view_environment_ids_param
cve = katello_content_view_environments(:library_dev_view_library)
cve.update(organization: @organization)
content_view_environment_ids = [cve.id]
ActivationKey.any_instance.expects(:reload)
assert_sync_task(::Actions::Katello::ActivationKey::Create) do |activation_key|
assert_equal content_view_environment_ids, activation_key.content_view_environments.map(&:id)
assert_valid activation_key
end

post :create, params: {
:organization_id => @organization.id,
:content_view_environment_ids => content_view_environment_ids,
:activation_key => {:name => 'new key'}
}

assert_response :success
assert_template 'katello/api/v2/common/create'
end

test_attributes :pid => 'a9e756e1-886d-4f0d-b685-36ce4247517d'
def test_should_not_create_with_no_hosts_limit
post :create, params: {
Expand All @@ -217,6 +257,16 @@ def test_should_not_create_with_no_hosts_limit
assert_match 'Validation failed: Max hosts cannot be nil', @response.body
end

def test_should_not_create_with_invalid_content_view_environments_param
post :create, params: { :organization_id => @organization.id, :content_view_environments => ['foo'] }
assert_response :unprocessable_entity
end

def test_should_not_create_with_invalid_content_view_environment_ids_param
post :create, params: { :organization_id => @organization.id, :content_view_environment_ids => ['foo'] }
assert_response :unprocessable_entity
end

test_attributes :pid => 'c018b177-2074-4f1a-a7e0-9f38d6c9a1a6'
def test_should_not_create_with_invalid_hosts_limit
post :create, params: {
Expand Down Expand Up @@ -300,6 +350,16 @@ def test_should_not_update_with_invalid_release
assert_response :bad_request
end

def test_should_not_update_with_invalid_content_view_environments_param
put :update, params: { :organization_id => @organization.id, :id => @activation_key.id, :content_view_environments => ['foo'] }
assert_response :unprocessable_entity
end

def test_should_not_update_with_invalid_content_view_environment_ids_param
put :update, params: { :organization_id => @organization.id, :id => @activation_key.id, :content_view_environment_ids => ['foo'] }
assert_response :unprocessable_entity
end

test_attributes :pid => 'ec225dad-2d27-4b37-989d-1ba2c7f74ac4'
def test_update_auto_attach
new_auto_attach = !@activation_key.auto_attach
Expand Down
53 changes: 53 additions & 0 deletions test/controllers/api/v2/hosts_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,59 @@ def test_host_update_with_cv_only
:content_view_id => @cv2.id
}
}, session: set_session_user
assert_response :unprocessable_entity
end

def test_set_content_view_environments_with_valid_content_view_environs_param
Katello::Host::SubscriptionFacet.any_instance.expects(:backend_update_needed?).returns(false)
::Host::Managed.any_instance.expects(:update_candlepin_associations)
host = FactoryBot.create(:host, :with_content, :with_subscription,
:content_view => @content_view, :lifecycle_environment => @environment)
::Katello::ContentViewEnvironment.expects(:fetch_content_view_environments).returns([katello_content_view_environments(:library_default_view_environment)])
put :update, params: {
:id => host.id,
:content_facet_attributes => {
:content_view_environments => ["Library"]
}
}, session: set_session_user
assert_response :success
end

def test_set_content_view_environments_with_valid_ids_param
Katello::Host::SubscriptionFacet.any_instance.expects(:backend_update_needed?).returns(false)
::Host::Managed.any_instance.expects(:update_candlepin_associations)
host = FactoryBot.create(:host, :with_content, :with_subscription,
:content_view => @content_view, :lifecycle_environment => @environment)
put :update, params: {
:id => host.id,
:content_facet_attributes => {
:content_view_environment_ids => [@cv4.content_view_environments.first.id]
}
}, session: set_session_user
assert_response :success
end

def test_set_content_view_environments_with_invalid_ids_param
host = FactoryBot.create(:host, :with_content, :with_subscription,
:content_view => @content_view, :lifecycle_environment => @environment)
put :update, params: {
:id => host.id,
:content_facet_attributes => {
:content_view_environment_ids => ["invalid string"]
}
}, session: set_session_user
assert_response :unprocessable_entity
end

def test_set_content_view_environments_with_invalid_content_view_environs_param
host = FactoryBot.create(:host, :with_content, :with_subscription,
:content_view => @content_view, :lifecycle_environment => @environment)
put :update, params: {
:id => host.id,
:content_facet_attributes => {
:content_view_environments => ["invalid string"]
}
}, session: set_session_user
assert_response 422
end

Expand Down
23 changes: 22 additions & 1 deletion test/models/content_view_environment_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,28 @@ def test_with_candlepin_name
dev = katello_environments(:dev)
view = katello_content_views(:library_dev_view)
cve = Katello::ContentViewEnvironment.where(:environment_id => dev, :content_view_id => view).first
assert_equal cve, ContentViewEnvironment.with_candlepin_name('dev_label/published_dev_view')
assert_equal cve, ContentViewEnvironment.with_candlepin_name('published_dev_view_dev', organization: dev.organization)
end

def test_fetch_content_view_environments_candlepin_names
dev = katello_environments(:dev)
view = katello_content_views(:library_dev_view)
cve = Katello::ContentViewEnvironment.where(:environment_id => dev, :content_view_id => view).first
assert_equal [cve], ContentViewEnvironment.fetch_content_view_environments(candlepin_names: ['published_dev_view_dev'], organization: dev.organization)
end

def test_fetch_content_view_environments_ids
dev = katello_environments(:dev)
view = katello_content_views(:library_dev_view)
cve = Katello::ContentViewEnvironment.where(:environment_id => dev, :content_view_id => view).first
assert_equal [cve], ContentViewEnvironment.fetch_content_view_environments(ids: [cve.id], organization: dev.organization)
end

def test_fetch_content_view_environments_invalid_ids_does_not_mutate_array
dev = katello_environments(:dev)
input_ids = [0, 999]
assert_equal [], ContentViewEnvironment.fetch_content_view_environments(ids: input_ids, organization: dev.organization)
assert_equal [0, 999], input_ids # should not have a map! which mutates the input array
end
end
end

0 comments on commit ffbebf3

Please sign in to comment.