Class: Authoreyes::Parser::AuthorizationRulesParser
- Inherits:
-
Object
- Object
- Authoreyes::Parser::AuthorizationRulesParser
- Defined in:
- lib/authoreyes/parser/authorization_rules_parser.rb
Overview
For examples and the modeled data model, see the README.
Also, see role definition methods
-
AuthorizationRulesReader#role,
-
AuthorizationRulesReader#includes,
-
AuthorizationRulesReader#title,
-
AuthorizationRulesReader#description
Methods for rule definition in roles
-
AuthorizationRulesReader#has_permission_on,
-
AuthorizationRulesReader#to,
-
AuthorizationRulesReader#if_attribute,
-
AuthorizationRulesReader#if_permitted_to
Methods to be used in if_attribute statements
-
AuthorizationRulesReader#contains,
-
AuthorizationRulesReader#does_not_contain,
-
AuthorizationRulesReader#intersects_with,
-
AuthorizationRulesReader#is,
-
AuthorizationRulesReader#is_not,
-
AuthorizationRulesReader#is_in,
-
AuthorizationRulesReader#is_not_in,
-
AuthorizationRulesReader#lt,
-
AuthorizationRulesReader#lte,
-
AuthorizationRulesReader#gt,
-
AuthorizationRulesReader#gte
And privilege definition methods
-
PrivilegesReader#privilege,
-
PrivilegesReader#includes
Instance Attribute Summary collapse
-
#auth_rules ⇒ Object
readonly
Returns the value of attribute auth_rules.
-
#omnipotent_roles ⇒ Object
readonly
Returns the value of attribute omnipotent_roles.
-
#role_descriptions ⇒ Object
readonly
Returns the value of attribute role_descriptions.
-
#role_hierarchy ⇒ Object
readonly
Returns the value of attribute role_hierarchy.
-
#role_titles ⇒ Object
readonly
Returns the value of attribute role_titles.
-
#roles ⇒ Object
readonly
Returns the value of attribute roles.
Instance Method Summary collapse
-
#append_role(role, options = {}) ⇒ Object
:nodoc:.
-
#contains(&block) ⇒ Object
In an if_attribute statement, contains says that the value has to be part of the collection specified by the if_attribute attribute.
-
#description(text) ⇒ Object
Sets a description for the current role.
-
#does_not_contain(&block) ⇒ Object
The negation of contains.
-
#gt(&block) ⇒ Object
Greater than.
-
#gte(&block) ⇒ Object
Greater than or equal to.
-
#has_omnipotence ⇒ Object
Removes any permission checks for the current role.
-
#has_permission_on(*args) ⇒ Object
Allows the definition of privileges to be allowed for the current role, either in a has_permission_on block or directly in one call.
-
#if_attribute(attr_conditions_hash) ⇒ Object
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.
-
#if_permitted_to(privilege, attr_or_hash = nil, options = {}) ⇒ Object
if_permitted_to allows the has_permission_on block to depend on permissions on associated objects.
-
#includes(*roles) ⇒ Object
Roles may inherit all the rights from subroles.
-
#initialize ⇒ AuthorizationRulesParser
constructor
:nodoc:.
-
#initialize_copy(from) ⇒ Object
:nodoc:.
-
#intersects_with(&block) ⇒ Object
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.
-
#is(&block) ⇒ Object
In an if_attribute statement, is says that the value has to be met exactly by the if_attribute attribute.
-
#is_in(&block) ⇒ Object
In an if_attribute statement, is_in says that the value has to contain the attribute value.
-
#is_not(&block) ⇒ Object
The negation of is.
-
#is_not_in(&block) ⇒ Object
The negation of is_in.
-
#lt(&block) ⇒ Object
Less than.
-
#lte(&block) ⇒ Object
Less than or equal to.
-
#role(role, options = {}) ⇒ Object
Defines the authorization rules for the given
rolein the following block. -
#title(text) ⇒ Object
Sets a human-readable title for the current role.
-
#to(*privs) ⇒ Object
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.
Constructor Details
#initialize ⇒ AuthorizationRulesParser
:nodoc:
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 39 def initialize # :nodoc: @current_role = nil @current_rule = nil @roles = [] @omnipotent_roles = [] # higher_role => [lower_roles] @role_hierarchy = {} @role_titles = {} @role_descriptions = {} @auth_rules = ::Authoreyes::Authorization::AuthorizationRuleSet.new end |
Instance Attribute Details
#auth_rules ⇒ Object (readonly)
Returns the value of attribute auth_rules.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def auth_rules @auth_rules end |
#omnipotent_roles ⇒ Object (readonly)
Returns the value of attribute omnipotent_roles.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def omnipotent_roles @omnipotent_roles end |
#role_descriptions ⇒ Object (readonly)
Returns the value of attribute role_descriptions.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def role_descriptions @role_descriptions end |
#role_hierarchy ⇒ Object (readonly)
Returns the value of attribute role_hierarchy.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def role_hierarchy @role_hierarchy end |
#role_titles ⇒ Object (readonly)
Returns the value of attribute role_titles.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def role_titles @role_titles end |
#roles ⇒ Object (readonly)
Returns the value of attribute roles.
36 37 38 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 36 def roles @roles end |
Instance Method Details
#append_role(role, options = {}) ⇒ Object
:nodoc:
64 65 66 67 68 69 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 64 def append_role(role, = {}) # :nodoc: @roles << role unless @roles.include? role @role_titles[role] = [:title] if [:title] @role_descriptions[role] = [:description] if [:description] end |
#contains(&block) ⇒ Object
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.
327 328 329 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 327 def contains(&block) [:contains, block] end |
#description(text) ⇒ Object
Sets a description for the current role. E.g.
role :admin
description "To be assigned to administrative personnel"
...
end
169 170 171 172 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 169 def description(text) raise DSLError, 'description only allowed in role blocks' if @current_role.nil? role_descriptions[@current_role] = text end |
#does_not_contain(&block) ⇒ Object
The negation of contains. Currently, query rewriting is disabled for does_not_contain.
333 334 335 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 333 def does_not_contain(&block) [:does_not_contain, block] end |
#gt(&block) ⇒ Object
Greater than
368 369 370 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 368 def gt(&block) [:gt, block] end |
#gte(&block) ⇒ Object
Greater than or equal to
373 374 375 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 373 def gte(&block) [:gte, block] end |
#has_omnipotence ⇒ Object
Removes any permission checks for the current role.
role :admin
has_omnipotence
end
159 160 161 162 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 159 def has_omnipotence raise DSLError, 'has_omnipotence only allowed in role blocks' if @current_role.nil? @omnipotent_roles << @current_role end |
#has_permission_on(*args) ⇒ Object
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
:employees, :to => :read
[:employees, :orders], :to => :read
:employees do
to :create
if_attribute ...
end
: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 :
andor :or. Defaults to :or.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 130 def (*args) = args. context = args.flatten raise DSLError, 'has_permission_on only allowed in role blocks' if @current_role.nil? = { to: [], join_by: :or }.merge() privs = [:to] privs = [privs] unless privs.is_a?(Array) raise DSLError, 'has_permission_on either needs a block or :to option' if !block_given? && privs.empty? file, line = file_and_line_number_from_call_stack rule = ::Authoreyes::Authorization::AuthorizationRule.new(@current_role, privs, context, [: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 |
#if_attribute(attr_conditions_hash) ⇒ Object
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
: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
: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.
: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]
251 252 253 254 255 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 251 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 ::Authoreyes::Authorization::Attribute.new(attr_conditions_hash) end |
#if_permitted_to(privilege, attr_or_hash = nil, options = {}) ⇒ Object
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
:branches, :to => :manage do
if_attribute :employees => contains { user }
end
: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:
:branches, :to => :manage do
if_attribute :employees => contains { user }
end
: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
303 304 305 306 307 308 309 310 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 303 def if_permitted_to(privilege, attr_or_hash = nil, = {}) raise DSLError, 'if_permitted_to only in has_permission blocks' if @current_rule.nil? [: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) && attr_or_hash.empty? @current_rule.append_attribute ::Authoreyes::Authorization::AttributeWithPermission.new(privilege, attr_or_hash, [:context]) end |
#includes(*roles) ⇒ Object
Roles may inherit all the rights from subroles. The given roles become subroles of the current block’s role.
role :admin do
includes :user
:employees, :to => [:update, :create]
end
role :user do
:employees, :to => :read
end
95 96 97 98 99 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 95 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 |
#initialize_copy(from) ⇒ Object
:nodoc:
51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 51 def initialize_copy(from) # :nodoc: [ :roles, :role_hierarchy, :auth_rules, :role_descriptions, :role_titles, :omnipotent_roles ].each do |attribute| instance_variable_set(:"@#{attribute}", from.send(attribute).clone) end end |
#intersects_with(&block) ⇒ Object
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.
341 342 343 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 341 def intersects_with(&block) [:intersects_with, block] end |
#is(&block) ⇒ Object
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.
315 316 317 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 315 def is(&block) [:is, block] end |
#is_in(&block) ⇒ Object
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.
348 349 350 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 348 def is_in(&block) [:is_in, block] end |
#is_not(&block) ⇒ Object
The negation of is.
320 321 322 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 320 def is_not(&block) [:is_not, block] end |
#is_not_in(&block) ⇒ Object
The negation of is_in.
353 354 355 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 353 def is_not_in(&block) [:is_not_in, block] end |
#lt(&block) ⇒ Object
Less than
358 359 360 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 358 def lt(&block) [:lt, block] end |
#lte(&block) ⇒ Object
Less than or equal to
363 364 365 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 363 def lte(&block) [:lte, block] end |
#role(role, options = {}) ⇒ Object
Defines the authorization rules for the given role in the following block.
role :admin do
...
end
77 78 79 80 81 82 83 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 77 def role(role, = {}) append_role role, @current_role = role yield ensure @current_role = nil end |
#title(text) ⇒ Object
Sets a human-readable title for the current role. E.g.
role :admin
title "Administrator"
...
end
179 180 181 182 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 179 def title(text) raise DSLError, 'title only allowed in role blocks' if @current_role.nil? role_titles[@current_role] = text end |
#to(*privs) ⇒ Object
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
:employees do
to :create, :read, :update, :delete
end
end
192 193 194 195 |
# File 'lib/authoreyes/parser/authorization_rules_parser.rb', line 192 def to(*privs) raise DSLError, 'to only allowed in has_permission_on blocks' if @current_rule.nil? @current_rule.append_privileges(privs.flatten) end |