Project

General

Profile

Download (4.08 KB) Statistics
| Branch: | Tag: | Revision:
module Authorizable
extend ActiveSupport::Concern

def check_permissions_after_save
return true if Thread.current[:ignore_permission_check]

authorizer = Authorizer.new(User.current)
creation = self.saved_change_to_id?
name = permission_name(creation ? :create : :edit)

Foreman::Logging.logger('permissions').debug { "verifying the transaction by permission #{name} for class #{self.class}" }
unless authorizer.can?(name, self)
org_loc_string = Taxonomy.enabled_taxonomies.map { |tax| _(tax) }.join(' ' + _('or') + ' ')
errors.add :base, _("You don't have permission %{name} with attributes that you have specified or you don't have access to specified %{tax_string}") % { :name => name, :tax_string => org_loc_string }

# This is required in case the rollback happend, the instance must look like new record so that all url helpers work correctly. Rails don't rollback these attributes.
if creation
self.id = nil
@new_record = true
end

# we need to rollback orchestration tasks if this object orchestrates something
if self.class.included_modules.include?(Orchestration)
self.send :fail_queue, self.queue
end

raise ActiveRecord::Rollback
end
end

def authorized?(permission)
return false if User.current.nil?
User.current.can?(permission, self)
end

def permission_name(action)
type = Permission.resource_name(self.class)
permissions = Permission.where(:resource_type => type).where(["#{Permission.table_name}.name LIKE ?", "#{action}_%"])

# some permissions are grouped for same resource, e.g. edit_comupute_resources and edit_compute_resources_vms, in such case we need to detect the right permission
if permissions.size > 1
permissions.detect { |p| p.name.end_with?(type.underscore.pluralize) }.try(:name)
else
permissions.first.try(:name)
end
end

included do
after_save :check_permissions_after_save
end

module ClassMethods
# permission can be nil (therefore we use Proc instead of lambda)
# same applies for resource class
#
# e.g.
# FactValue.authorized_as(user)
# FactValue.authorized_as(user, :view_facts)
# Host::Base.authorized_as(user, :view_hosts, Host)
#
# Or you may simply use authorized for User.current
def authorized_as(user, permission, resource = nil)
if user.nil?
self.where('1=0')
elsif user.admin?
self.where(nil)
else
Authorizer.new(user).find_collection(resource || self, :permission => permission)
end
end

# joins to another class, on which the authorization is applied
#
# permission can be nil (therefore we use Proc instead of lambda)
#
# e.g.
# Report.joins_authorized_as(user, Host, :view_hosts)
# Host.joins_authorized_as(user, Domain, :view_domains)
#
# Or you may simply use authorized for User.current
#
# The default scope of `resource` is NOT applied since it's a join, instead
# any extra conditions can be given in `opts[:where]`.
#
def joins_authorized_as(user, resource, permission, opts = {})
if user.nil?
self.where('1=0')
else
Authorizer.new(user).find_collection(resource, {:permission => permission, :joined_on => self}.merge(opts))
end
end

def allows_taxonomy_filtering?(taxonomy)
scoped_search_definition&.fields&.has_key?(taxonomy)
end

def allows_organization_filtering?
allows_taxonomy_filtering?(:organization_id)
end

def allows_location_filtering?
allows_taxonomy_filtering?(:location_id)
end

def authorized(permission = nil, resource = nil)
authorized_as(User.current, permission, resource)
end

def joins_authorized(resource, permission = nil, opts = {})
joins_authorized_as(User.current, resource, permission, opts)
end

def skip_permission_check
original_value, Thread.current[:ignore_permission_check] = Thread.current[:ignore_permission_check], true
yield
ensure
Thread.current[:ignore_permission_check] = original_value
end
end
end
(4-4/48)