class Authorization::Reader::AuthorizationRulesReader
Public Instance Methods
In an if_attribute
statement, contains says that the value has to be part of the collection specified by the if_attribute
attribute. For information on the block argument, see if_attribute.
# File lib/declarative_authorization/reader.rb, line 473 def contains (&block) [:contains, block] end
Sets a description for the current role. E.g.
role :admin description "To be assigned to administrative personnel" has_permission_on ... end
# File lib/declarative_authorization/reader.rb, line 315 def description (text) raise DSLError, "description only allowed in role blocks" if @current_role.nil? role_descriptions[@current_role] = text end
The negation of contains. Currently, query rewriting is disabled for does_not_contain.
# File lib/declarative_authorization/reader.rb, line 479 def does_not_contain (&block) [:does_not_contain, block] end
Greater than
# File lib/declarative_authorization/reader.rb, line 514 def gt (&block) [:gt, block] end
Greater than or equal to
# File lib/declarative_authorization/reader.rb, line 519 def gte (&block) [:gte, block] end
Removes any permission checks for the current role.
role :admin has_omnipotence end
# File lib/declarative_authorization/reader.rb, line 305 def has_omnipotence raise DSLError, "has_omnipotence only allowed in role blocks" if @current_role.nil? @omnipotent_roles << @current_role end
Allows the definition of privileges to be allowed for the current role, either in a has_permission_on
block or directly in one call.
role :admin has_permission_on :employees, :to => :read has_permission_on [:employees, :orders], :to => :read has_permission_on :employees do to :create if_attribute ... end has_permission_on :employees, :to => :delete do if_attribute ... end end
The block form allows to describe restrictions on the permissions using if_attribute. Multiple has_permission_on
statements are OR'ed when evaluating the permissions. Also, multiple if_attribute
statements in one block are OR'ed if no :join_by
option is given (see below). To AND conditions, either set :join_by
to :and or place them in one if_attribute
statement.
Available options
- :
to
-
A symbol or an array of symbols representing the privileges that should be granted in this statement.
- :
join_by
-
Join operator to logically connect the constraint statements inside of the
has_permission_on
block. May be :and
or :or
. Defaults to :or
.
# File lib/declarative_authorization/reader.rb, line 277 def has_permission_on (*args, &block) options = args.extract_options! context = args.flatten raise DSLError, "has_permission_on only allowed in role blocks" if @current_role.nil? options = {:to => [], :join_by => :or}.merge(options) privs = options[:to] privs = [privs] unless privs.is_a?(Array) raise DSLError, "has_permission_on either needs a block or :to option" if !block_given? and privs.empty? file, line = file_and_line_number_from_call_stack rule = AuthorizationRule.new(@current_role, privs, context, options[:join_by], :source_file => file, :source_line => line) @auth_rules << rule if block_given? @current_rule = rule yield raise DSLError, "has_permission_on block content specifies no privileges" if rule.privileges.empty? # TODO ensure? @current_rule = nil end end
In a has_permission_on
block, if_attribute
specifies conditions of dynamic parameters that have to be met for the user to meet the privileges in this block. Conditions are evaluated on the context object. Thus, the following allows CRUD for branch admins only on employees that belong to the same branch as the current user.
role :branch_admin has_permission_on :employees do to :create, :read, :update, :delete if_attribute :branch => is { user.branch } end end
In this case, is is the operator for evaluating the condition. Another operator is contains for collections. In the block supplied to the operator, user
specifies the current user for whom the condition is evaluated.
Conditions may be nested:
role :company_admin has_permission_on :employees do to :create, :read, :update, :delete if_attribute :branch => { :company => is {user.branch.company} } end end
has_many and has_many through associations may also be nested. Then, at least one item in the association needs to fulfill the subsequent condition:
if_attribute :company => { :branches => { :manager => { :last_name => is { user.last_name } } }
Beware of possible performance issues when using has_many associations in permitted_to? checks. For
permitted_to? :read, object
a check like
object.company.branches.any? { |branch| branch.manager ... }
will be executed. with_permission_to scopes construct efficient SQL joins, though.
Multiple attributes in one :if_attribute statement are AND'ed. Multiple if_attribute
statements are OR'ed if the join operator for the has_permission_on
block isn't explicitly set. Thus, the following would require the current user either to be of the same branch AND the employee to be “changeable_by_coworker”. OR the current user has to be the employee in question.
has_permission_on :employees, :to => :manage do if_attribute :branch => is {user.branch}, :changeable_by_coworker => true if_attribute :id => is {user.id} end
The join operator for if_attribute
rules can explicitly set to AND, though. See has_permission_on
for details.
Arrays and fixed values may be used directly as hash values:
if_attribute :id => 1 if_attribute :type => "special" if_attribute :id => [1,2]
# File lib/declarative_authorization/reader.rb, line 397 def if_attribute (attr_conditions_hash) raise DSLError, "if_attribute only in has_permission blocks" if @current_rule.nil? parse_attribute_conditions_hash!(attr_conditions_hash) @current_rule.append_attribute Attribute.new(attr_conditions_hash) end
if_permitted_to
allows the has_permission_on
block to depend on permissions on associated objects. By using it, the authorization rules may be a lot DRYer. E.g.:
role :branch_manager has_permission_on :branches, :to => :manage do if_attribute :employees => contains { user } end has_permission_on :employees, :to => :read do if_permitted_to :read, :branch # instead of # if_attribute :branch => { :employees => contains { user } } end end
if_permitted_to
associations may be nested as well:
if_permitted_to :read, :branch => :company
You can even use has_many associations as target. Then, it is checked if the current user has the required privilege on any of the target objects.
if_permitted_to :read, :branch => :employees
Beware of performance issues with permission checks. In the current implementation, all employees are checked until the first permitted is found. with_permissions_to, on the other hand, constructs more efficient SQL instead.
To check permissions based on the current object, the attribute has to be left out:
has_permission_on :branches, :to => :manage do if_attribute :employees => contains { user } end has_permission_on :branches, :to => :paint_green do if_permitted_to :update end
Normally, one would merge those rules into one. Dividing makes sense if additional if_attribute
are used in the second rule or those rules are applied to different roles.
Options:
- :
context
-
When using with_permissions_to, the target context of the
if_permitted_to
statement is inferred from the last reflections target class. Still, you may override this algorithm by setting the context explicitly.if_permitted_to :read, :home_branch, :context => :branches if_permitted_to :read, :branch => :main_company, :context => :companies
# File lib/declarative_authorization/reader.rb, line 449 def if_permitted_to (privilege, attr_or_hash = nil, options = {}) raise DSLError, "if_permitted_to only in has_permission blocks" if @current_rule.nil? options[:context] ||= attr_or_hash.delete(:context) if attr_or_hash.is_a?(Hash) # only :context option in attr_or_hash: attr_or_hash = nil if attr_or_hash.is_a?(Hash) and attr_or_hash.empty? @current_rule.append_attribute AttributeWithPermission.new(privilege, attr_or_hash, options[:context]) end
Roles may inherit all the rights from subroles. The given roles
become subroles of the current block's role.
role :admin do includes :user has_permission_on :employees, :to => [:update, :create] end role :user do has_permission_on :employees, :to => :read end
# File lib/declarative_authorization/reader.rb, line 243 def includes (*roles) raise DSLError, "includes only in role blocks" if @current_role.nil? @role_hierarchy[@current_role] ||= [] @role_hierarchy[@current_role] += roles.flatten end
In an if_attribute
statement, intersects_with
requires that at least one of the values has to be part of the collection specified by the if_attribute
attribute. The value block needs to evaluate to an Enumerable. For information on the block argument, see if_attribute.
# File lib/declarative_authorization/reader.rb, line 487 def intersects_with (&block) [:intersects_with, block] end
In an if_attribute
statement, is says that the value has to be met exactly by the if_attribute
attribute. For information on the block argument, see if_attribute.
# File lib/declarative_authorization/reader.rb, line 461 def is (&block) [:is, block] end
In an if_attribute
statement, is_in
says that the value has to contain the attribute value. For information on the block argument, see if_attribute.
# File lib/declarative_authorization/reader.rb, line 494 def is_in (&block) [:is_in, block] end
The negation of is.
# File lib/declarative_authorization/reader.rb, line 466 def is_not (&block) [:is_not, block] end
The negation of is_in.
# File lib/declarative_authorization/reader.rb, line 499 def is_not_in (&block) [:is_not_in, block] end
Less than
# File lib/declarative_authorization/reader.rb, line 504 def lt (&block) [:lt, block] end
Less than or equal to
# File lib/declarative_authorization/reader.rb, line 509 def lte (&block) [:lte, block] end
Defines the authorization rules for the given role
in the following block.
role :admin do has_permissions_on ... end
# File lib/declarative_authorization/reader.rb, line 225 def role (role, options = {}, &block) append_role role, options @current_role = role yield ensure @current_role = nil end
Sets a human-readable title for the current role. E.g.
role :admin title "Administrator" has_permission_on ... end
# File lib/declarative_authorization/reader.rb, line 325 def title (text) raise DSLError, "title only allowed in role blocks" if @current_role.nil? role_titles[@current_role] = text end
Used in a has_permission_on
block, to may be used to specify privileges to be assigned to the current role under the conditions specified in the current block.
role :admin has_permission_on :employees do to :create, :read, :update, :delete end end
# File lib/declarative_authorization/reader.rb, line 338 def to (*privs) raise DSLError, "to only allowed in has_permission_on blocks" if @current_rule.nil? @current_rule.append_privileges(privs.flatten) end
Private Instance Methods
# File lib/declarative_authorization/reader.rb, line 538 def file_and_line_number_from_call_stack caller_parts = caller(2).first.split(':') [caller_parts[0] == "(eval)" ? nil : caller_parts[0], caller_parts[1] && caller_parts[1].to_i] end
# File lib/declarative_authorization/reader.rb, line 524 def parse_attribute_conditions_hash! (hash) merge_hash = {} hash.each do |key, value| if value.is_a?(Hash) parse_attribute_conditions_hash!(value) elsif !value.is_a?(Array) merge_hash[key] = [:is, proc { value }] elsif value.is_a?(Array) and !value[0].is_a?(Symbol) merge_hash[key] = [:is_in, proc { value }] end end hash.merge!(merge_hash) end