Project

General

Profile

« Previous | Next » 

Revision 08d4fc31

Added by Ori Rabin over 8 years ago

Fixes #11188 - EnsureNotUsedBy checks for unscoped associations before destroying

View differences:

app/helpers/layout_helper.rb
# add hidden field for options[:disabled]
def multiple_selects(f, attr, associations, selected_ids, options = {}, html_options = {})
options.merge!(:size => "col-md-10")
authorized = authorized_associations(associations).all
authorized = AssociationAuthorizer.authorized_associations(associations).all
unauthorized = selected_ids.blank? ? [] : selected_ids - authorized.map(&:id)
field(f, attr, options) do
attr_ids = (attr.to_s.singularize+"_ids").to_sym
......
private
def authorized_associations(associations)
if associations.included_modules.include?(Authorizable)
if associations.respond_to?(:klass)
associations.authorized(authorized_associations_permission_name(associations.klass), associations.klass)
else
associations.authorized(authorized_associations_permission_name(associations), associations)
end
else
associations
end
end
def authorized_associations_permission_name(klass)
permission = "view_#{klass.to_s.underscore.pluralize}"
unless Permission.where(:name => permission).present?
raise Foreman::Exception.new(N_('unknown permission %s'), permission)
end
permission
end
def table_css_classes(classes = '')
"table table-bordered table-striped table-condensed " + classes
end
app/models/smart_proxy.rb
attr_accessible :name, :url, :location_ids, :organization_ids
validates_lengths_from_database
before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups, :subnets, :domains, :puppet_ca_hosts, :puppet_ca_hostgroups, :realms)
before_destroy EnsureNotUsedBy.new(:hosts, :hostgroups, :subnets, :domains, [:puppet_ca_hosts, :hosts], [:puppet_ca_hostgroups, :hostgroups], :realms)
#TODO check if there is a way to look into the tftp_id too
# maybe with a predefined sql
has_and_belongs_to_many :features
app/models/user.rb
attr_protected :password_hash, :password_salt, :admin
attr_accessor :password, :password_confirmation
after_save :ensure_default_role
before_destroy EnsureNotUsedBy.new(:direct_hosts), :ensure_hidden_users_are_not_deleted, :ensure_last_admin_is_not_deleted
before_destroy EnsureNotUsedBy.new([:direct_hosts, :hosts]), :ensure_hidden_users_are_not_deleted, :ensure_last_admin_is_not_deleted
belongs_to :auth_source
belongs_to :default_organization, :class_name => 'Organization'
app/services/association_authorizer.rb
class AssociationAuthorizer
def self.authorized_associations(associations, klass_name = nil, should_raise_exception = true)
if associations.included_modules.include?(Authorizable)
associations_klass = associations
if associations.respond_to?(:klass)
associations_klass = associations.klass
end
klass_name ||= associations_klass
permission = view_permission_name(klass_name, should_raise_exception)
associations.authorized(permission, associations_klass) if permission
else
associations
end
end
def self.view_permission_name(klass, should_raise_exception)
permission = "view_#{klass.to_s.underscore.pluralize}"
if Permission.where(:name => permission).present?
permission
elsif should_raise_exception
raise Foreman::Exception.new(N_('unknown permission %s'), permission)
else
false
end
end
end
lib/core_extensions.rb
end
def before_destroy(record)
klasses.each do |klass|
record.send(klass.to_sym).each do |what|
what = what.to_label unless what.is_a? String
record.errors.add :base, _("%{record} is used by %{what}") % { :record => record, :what => what }
klasses.each do |klass, klass_name = klass|
record.association(klass.to_sym).association_scope.each do |what|
error_message = _("%{record} is used by %{what}")
unless what.is_a? String
authorized_associations = AssociationAuthorizer.authorized_associations(record.class.reflect_on_association(klass.to_sym).klass, klass_name, false)
if !authorized_associations.respond_to?(:to_a) || authorized_associations.to_a.include?(what)
what = what.to_label
else
what = _(what.class.name)
error_message = _("%{record} is being used by a hidden %{what} resource")
end
end
record.errors.add :base, error_message % { :record => record, :what => what }
end
end
if record.errors.empty?
test/unit/association_authorizer_test.rb
require 'test_helper'
class AssociationAuthorizerTest < ActiveSupport::TestCase
def setup
@hostgroup = FactoryGirl.create(:hostgroup)
@host = FactoryGirl.create(:host, :managed, :hostgroup => @hostgroup)
@user = FactoryGirl.create(:user)
end
test "user with permissions can view host" do
role = FactoryGirl.create(:role, :name => 'can_view_host')
role.add_permissions!(['view_hosts'])
@user.update_attribute :roles, [role]
as_user @user do
authorized = AssociationAuthorizer.authorized_associations(Hostgroup.reflect_on_association(:hosts).klass, :hosts)
assert authorized.include?(@host)
end
end
test "user without permissions can't view host" do
as_user @user do
authorized = AssociationAuthorizer.authorized_associations(Hostgroup.reflect_on_association(:hosts).klass, :hosts)
refute authorized.include?(@host)
end
end
test "authorized_associations should raise unknown permission exception when should_raise_exception is true" do
assert_raise(Foreman::Exception) do
AssociationAuthorizer.view_permission_name('non_existing_permission', true)
end
end
test "authorized_associations should return false for unknown permission when should_raise_exception is false" do
permission = AssociationAuthorizer.view_permission_name('non_existing_permission', false)
assert_equal false, permission
end
test "authorized_associations should return permission if it exists" do
permission = AssociationAuthorizer.view_permission_name(:host, false)
assert_equal "view_hosts", permission
end
end
test/unit/ensure_not_used_by_test.rb
require 'test_helper'
class EnsureNotUsedByTest < ActiveSupport::TestCase
def setup
@org1 = FactoryGirl.create(:organization)
@org2 = FactoryGirl.create(:organization)
@user = FactoryGirl.build(:user, :with_mail, :organizations => [@org1])
@user.save(:validate => false)
role = FactoryGirl.create(:role, :name => 'can_view_host')
role.add_permissions!(['view_hosts'])
@user.update_attribute :roles, [role]
end
test "hostgroup should not be deleted if used by host in user org" do
hostgroup = FactoryGirl.create(:hostgroup, :organizations => [@org1, @org2])
host = FactoryGirl.create(:host, :managed, :hostgroup => hostgroup, :organization => @org1)
as_user @user do
in_taxonomy @org1 do
refute hostgroup.destroy
assert_equal "#{hostgroup.name} is used by #{host.name}", hostgroup.errors.full_messages.first
end
end
end
test "hostgroup should not be deleted if used by host in different org" do
hostgroup = FactoryGirl.create(:hostgroup, :organizations => [@org1, @org2])
FactoryGirl.create(:host, :hostgroup => hostgroup, :organization => @org2)
as_user @user do
in_taxonomy @org1 do
refute hostgroup.destroy
assert_equal "#{hostgroup.name} is being used by a hidden Host::Managed resource", hostgroup.errors.full_messages.first
end
end
end
test "hostgroup should not be deleted if used by host" do
hostgroup = FactoryGirl.create(:hostgroup, :organizations => [@org1, @org2])
FactoryGirl.create(:host, :hostgroup => hostgroup, :organization => @org2)
as_user FactoryGirl.create(:user, :with_mail) do
in_taxonomy @org1 do
refute hostgroup.destroy
assert_equal "#{hostgroup.name} is being used by a hidden Host::Managed resource", hostgroup.errors.full_messages.first
end
end
end
test "hostgroup should be deleted if not used by host" do
hostgroup = FactoryGirl.create(:hostgroup, :organizations => [@org1, @org2])
FactoryGirl.create(:host, :organization => @org2)
as_user @user do
assert hostgroup.destroy
end
end
test "host using hostgroup should not be shown to user without permissions" do
hostgroup = FactoryGirl.create(:hostgroup)
FactoryGirl.create(:host, :managed, :hostgroup => hostgroup)
as_user FactoryGirl.create(:user, :with_mail) do
refute hostgroup.destroy
assert_equal "#{hostgroup.name} is being used by a hidden Host::Managed resource", hostgroup.errors.full_messages.first
end
end
end

Also available in: Unified diff