Class: Rend::Acl
- Inherits:
-
Object
- Object
- Rend::Acl
- Includes:
- Core::Helpers::Php
- Defined in:
- lib/rend/acl.rb,
lib/rend/acl/role.rb,
lib/rend/acl/version.rb,
lib/rend/acl/resource.rb,
lib/rend/acl/assertion.rb,
lib/rend/acl/exception.rb,
lib/rend/acl/role/registry.rb,
lib/rend/acl/role/registry/exception.rb
Defined Under Namespace
Modules: Version Classes: Assertion, Exception, Resource, Role
Constant Summary collapse
- TYPE_ALLOW =
:TYPE_ALLOW
- TYPE_DENY =
:TYPE_DENY
- OP_ADD =
:OP_ADD
- OP_REMOVE =
:OP_REMOVE
Instance Method Summary collapse
-
#add!(*args) ⇒ Object
Adds Roles & Resources in various ways.
-
#add_resource!(resource, parent = nil) ⇒ Object
Adds a Resource having an identifier unique to the ACL.
-
#add_role!(role, parents = nil) ⇒ Object
Adds a Role having an identifier unique to the registry.
-
#allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Adds an “allow” rule to the ACL.
-
#allowed?(role = nil, resource = nil, privilege = nil) ⇒ Boolean
Returns true if and only if the Role has access to the Resource.
-
#denied?(*args) ⇒ Boolean
Inverse of allowed? method.
-
#deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Adds a “deny” rule to the ACL.
-
#inherits_resource?(resource, inherit, only_parent = false) ⇒ Boolean
Returns true if and only if resource inherits from inherit.
-
#inherits_role?(role, inherit, only_parents = false) ⇒ Boolean
Returns true if and only if role inherits from inherit.
-
#initialize ⇒ Acl
constructor
A new instance of Acl.
-
#remove_allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Removes “allow” permissions from the ACL.
-
#remove_deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Removes “deny” restrictions from the ACL.
-
#remove_resource!(resource) ⇒ Object
Removes a Resource and all of its children.
-
#remove_resource_all! ⇒ Object
Removes all Resources.
-
#remove_role!(role) ⇒ Object
Removes the Role from the registry.
-
#remove_role_all! ⇒ Object
Removes all Roles from the registry.
-
#resource!(resource) ⇒ Object
Returns the identified Resource.
-
#resource?(resource) ⇒ Boolean
Returns true if and only if the Resource exists in the ACL.
-
#resources ⇒ Object
Array of registered resources.
-
#role!(role) ⇒ Object
Returns the identified Role.
-
#role?(role) ⇒ Boolean
Returns true if and only if the Role exists in the registry.
-
#role_registry ⇒ Object
Returns the Role registry for this ACL.
-
#roles ⇒ Object
Returns an array of registered roles.
-
#set_rule!(operation, type, roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Performs operations on ACL rules.
Constructor Details
#initialize ⇒ Acl
Returns a new instance of Acl.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rend/acl.rb', line 18 def initialize # @var Rend::Acl::Role::Registry @_role_registry = nil # @var Hash @_resources = {} # @var Rend::Acl::Role @_is_allowed_role = nil # @var Rend::Acl::Resource @_is_allowed_resource = nil # @var String @_is_allowed_privilege = nil # ACL rules whitelist (deny everything to all) by default # @var Hash @_rules = { :all_resources => { :all_roles => { :all_privileges => { :type => TYPE_DENY, :assertion => nil }, :by_privilege_id => {} }, :by_role_id => {} }, :by_resource_id => {} } end |
Instance Method Details
#add!(*args) ⇒ Object
Adds Roles & Resources in various ways.
-
Roles
-
Arguments .add! Rend::Acl::Role.new(“editor”) # Single Role .add! Rend::Acl::Role.new(“editor”), ‘guest’ # Single Role w/ Single Inheritance .add! Rend::Acl::Role.new(“editor”), [‘guest’, ‘contributor’] # Single Role w/ Multiple Inheritance
-
Hash .add! :role => ‘editor’ # Single Role .add! :role => => ‘guest’ # Single Role w/ Single Inheritance .add! :role => => [‘guest’, ‘contributor’] # Single Role w/ Multiple Inheritance .add! :role => [‘guest’, ‘editor’] # Multiple Roles .add! :role => [‘guest’, ‘contributor’, => ‘guest’] # Multiple Roles w/ Single Inheritance .add! :role => [‘guest’, ‘contributor’, => [‘guest’, ‘contributor’]] # Multiple Roles w/ Multiple Inheritance
-
-
Resources
-
Arguments .add! Rend::Acl::Resource.new(“city”) # Single Resource .add! Rend::Acl::Resource.new(“building”), ‘city’ # Single Resource w/ Inheritance
-
Hash .add! :resource => ‘city’ # Single Resource .add! :resource => => ‘city’ # Single Resource w/ Inheritance .add! :resource => [‘city’, ‘building’] # Multiple Resources .add! :resource => [‘city’, ‘building’, => ‘city’] # Multiple Resources w/ Inheritance
-
-
Combined Roles & Resources
.add! :role => ['guest', {'editor' => 'guest'}], :resource => ['city', {'building' => 'city'}]
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/rend/acl.rb', line 77 def add!(*args) raise ArgumentError, "wrong number of arguments(0 for 1..2)" if args.empty? method_args = {:role => [], :resource => []} case args[0] when Rend::Acl::Role then method_args[:role] << args when Rend::Acl::Resource then method_args[:resource] << args when Hash args[0].each do |key, value| if [:role, :resource].include?(key.to_sym) case value when String then method_args[key] << value when Hash then method_args[key] << value.flatten when Array then value.each {|x| method_args[key] << (x.is_a?(Hash) ? x.flatten : x) } else raise Rend::Acl::Exception, "Invalid value (#{value.inspect}) for key (#{key.to_s}) in options hash." end else raise Rend::Acl::Exception, "Invalid key (#{key.to_s}) in options hash." end end else raise Rend::Acl::Exception, "First argument is not an instance of Rend::Acl::Role, Rend::Acl::Resource, or Hash." end method_args.each do |type, arguments| method = "add_#{type.to_s}!".to_sym arguments.each {|args| send(method, *args)} end self end |
#add_resource!(resource, parent = nil) ⇒ Object
Adds a Resource having an identifier unique to the ACL
The parent parameter may be a reference to, or the string identifier for, the existing Resource from which the newly added Resource will inherit.
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/rend/acl.rb', line 230 def add_resource!(resource, parent = nil) resource = Rend::Acl::Resource.new(resource) if resource.is_a?(String) type_hint! Rend::Acl::Resource, resource, :is_required => true resource_id = resource.id raise Rend::Acl::Exception, "Resource id 'resource_id' already exists in the ACL" if resource?(resource_id) resource_parent = nil if parent begin resource_parent_id = (parent.class <= Rend::Acl::Resource) ? parent.id : parent resource_parent = resource!(resource_parent_id) rescue Rend::Acl::Exception raise Rend::Acl::Exception, "Parent Resource id 'resource_parent_id' does not exist" end @_resources[resource_parent_id][:children][resource_id] = resource end @_resources[resource_id] = { :instance => resource, :parent => resource_parent, :children => {} } self end |
#add_role!(role, parents = nil) ⇒ Object
Adds a Role having an identifier unique to the registry
The parents parameter may be a reference to, or the string identifier for, a Role existing in the registry, or parents may be passed as an array of these - mixing string identifiers and objects is ok - to indicate the Roles from which the newly added Role will directly inherit.
In order to resolve potential ambiguities with conflicting rules inherited from different parents, the most recently added parent takes precedence over parents that were previously added. In other words, the first parent added will have the least priority, and the last parent added will have the highest priority.
124 125 126 127 128 129 |
# File 'lib/rend/acl.rb', line 124 def add_role!(role, parents = nil) role = Rend::Acl::Role.new(role) if role.is_a?(String) type_hint! Rend::Acl::Role, role, :is_required => true role_registry.add!(role, parents) self end |
#allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Adds an “allow” rule to the ACL
367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/rend/acl.rb', line 367 def allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) if roles.is_a?(Hash) = roles roles = .fetch(:role, nil) resources = .fetch(:resource, nil) privileges = .fetch(:privilege, nil) assertion = .fetch(:assertion, nil) end type_hint! Rend::Acl::Assertion, assertion set_rule!(OP_ADD, TYPE_ALLOW, roles, resources, privileges, assertion) end |
#allowed?(role = nil, resource = nil, privilege = nil) ⇒ Boolean
Returns true if and only if the Role has access to the Resource
The role and resource parameters may be references to, or the string identifiers for, an existing Resource and Role combination.
If either role or resource is nil, then the query applies to all Roles or all Resources, respectively. Both may be nil to query whether the ACL has a “blacklist” rule (allow everything to all). By default, Rend::Acl creates a “whitelist” rule (deny everything to all), and this method would return false unless this default has been overridden (i.e., by executing acl->allow()).
If a privilege is not provided, then this method returns false if and only if the Role is denied access to at least one privilege upon the Resource. In other words, this method returns true if and only if the Role is allowed all privileges on the Resource.
This method checks Role inheritance using a depth-first traversal of the Role registry. The highest priority parent (i.e., the parent most recently added) is checked first, and its respective parents are checked similarly before the lower-priority parents of the Role are checked.
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
# File 'lib/rend/acl.rb', line 540 def allowed?(role = nil, resource = nil, privilege = nil) # reset role & resource to nil @_is_allowed_role = nil @_is_allowed_resource = nil @_is_allowed_privilege = nil # Readability if role.is_a?(Hash) = role role = .fetch(:role, nil) resource = .fetch(:resource, nil) privilege = .fetch(:privilege, nil) end if role # keep track of originally called role @_is_allowed_role = role role = role_registry.get!(role) @_is_allowed_role = role unless @_is_allowed_role.class <= Rend::Acl::Role end if resource # keep track of originally called resource @_is_allowed_resource = resource resource = resource!(resource) unless @_is_allowed_resource.class <= Rend::Acl::Resource @_is_allowed_resource = resource end end if privilege.nil? # query on all privileges loop do # loop terminates at :all_resources pseudo-parent # depth-first search on role if it is not :all_roles pseudo-parent if !role.nil? && !(result = _role_dfs_all_privileges(role, resource)).nil? return result end # look for rule on :all_roles psuedo-parent rules = _rules(resource, nil) if rules rules[:by_privilege_id].each do |priv, rule| rule_type_one_privilege = _rule_type(resource, nil, priv) return false if rule_type_one_privilege == TYPE_DENY end rule_type_one_privilege = _rule_type(resource, nil, nil) return rule_type_one_privilege == TYPE_ALLOW if rule_type_one_privilege end # try next Resource resource = @_resources[resource.id][:parent] end else @_is_allowed_privilege = privilege # query on one privilege loop do # loop terminates at :all_resources pseudo-parent # depth-first search on role if it is not :all_roles pseudo-parent if !role.nil? && !(result = _role_dfs_one_privilege(role, resource, privilege)).nil? return result end # look for rule on 'allRoles' pseudo-parent if nil != (rule_type = _rule_type(resource, nil, privilege)) return TYPE_ALLOW == rule_type elsif nil != (rule_type_all_privileges = _rule_type(resource, nil, nil)) return TYPE_ALLOW == rule_type_all_privileges end # try next Resource resource = @_resources[resource.id][:parent] end end end |
#denied?(*args) ⇒ Boolean
Inverse of allowed? method
617 618 619 |
# File 'lib/rend/acl.rb', line 617 def denied?(*args) !allowed?(*args) end |
#deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Adds a “deny” rule to the ACL
389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'lib/rend/acl.rb', line 389 def deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) if roles.is_a?(Hash) = roles roles = .fetch(:role, nil) resources = .fetch(:resource, nil) privileges = .fetch(:privilege, nil) assertion = .fetch(:assertion, nil) end type_hint! Rend::Acl::Assertion, assertion set_rule!(OP_ADD, TYPE_DENY, roles, resources, privileges, assertion) end |
#inherits_resource?(resource, inherit, only_parent = false) ⇒ Boolean
Returns true if and only if resource inherits from inherit
Both parameters may be either a Resource or a Resource identifier. If only_parent is true, then resource must inherit directly from inherit in order to return true. By default, this method looks through the entire inheritance tree to determine whether resource inherits from inherit through its ancestor Resources.
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/rend/acl.rb', line 292 def inherits_resource?(resource, inherit, only_parent = false) resource_id = resource!(resource).id inherit_id = resource!(inherit).id if @_resources[resource_id][:parent] parent_id = @_resources[resource_id][:parent].id return true if inherit_id == parent_id return false if only_parent else return false end while @_resources[parent_id][:parent] parent_id = @_resources[parent_id][:parent].id return true if inherit_id == parent_id end false end |
#inherits_role?(role, inherit, only_parents = false) ⇒ Boolean
Returns true if and only if role inherits from inherit
Both parameters may be either a Role or a Role identifier. If only_parents is true, then role must inherit directly from inherit in order to return true. By default, this method looks through the entire inheritance DAG to determine whether role inherits from inherit through its ancestor Roles.
166 167 168 |
# File 'lib/rend/acl.rb', line 166 def inherits_role?(role, inherit, only_parents = false) role_registry.inherits?(role, inherit, only_parents) end |
#remove_allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Removes “allow” permissions from the ACL
410 411 412 413 414 415 416 417 418 419 420 |
# File 'lib/rend/acl.rb', line 410 def remove_allow!(roles = nil, resources = nil, privileges = nil, assertion = nil) if roles.is_a?(Hash) = roles roles = .fetch(:role, nil) resources = .fetch(:resource, nil) privileges = .fetch(:privilege, nil) assertion = .fetch(:assertion, nil) end set_rule!(OP_REMOVE, TYPE_ALLOW, roles, resources, privileges, assertion) end |
#remove_deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Removes “deny” restrictions from the ACL
429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/rend/acl.rb', line 429 def remove_deny!(roles = nil, resources = nil, privileges = nil, assertion = nil) if roles.is_a?(Hash) = roles roles = .fetch(:role, nil) resources = .fetch(:resource, nil) privileges = .fetch(:privilege, nil) assertion = .fetch(:assertion, nil) end set_rule!(OP_REMOVE, TYPE_DENY, roles, resources, privileges, assertion) end |
#remove_resource!(resource) ⇒ Object
Removes a Resource and all of its children
The resource parameter can either be a Resource or a Resource identifier.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/rend/acl.rb', line 318 def remove_resource!(resource) resource_id = resource!(resource).id resources_removed = [resource_id] if resource_parent = @_resources[resource_id][:parent] @_resources[resource_parent.id][:children].delete(resource_id) end @_resources[resource_id][:children].each do |child_id, child| remove_resource!(child_id) resources_removed.push(child_id) end resources_removed.each do |resource_id_removed| @_rules[:by_resource_id].each do |resource_id_current, rules| if resource_id_removed == resource_id_current @_rules[:by_resource_id].delete(resource_id_current) end end end @_resources.delete(resource_id) self end |
#remove_resource_all! ⇒ Object
Removes all Resources
347 348 349 350 351 352 353 354 355 356 357 |
# File 'lib/rend/acl.rb', line 347 def remove_resource_all! @_resources.each do |resource_id, resource| @_rules[:by_resource_id].each do |resource_id_current, rules| @_rules[:by_resource_id].delete(resource_id_current) if resource_id == resource_id_current end end @_resources = {} self end |
#remove_role!(role) ⇒ Object
Removes the Role from the registry
The role parameter can either be a Role or a Role identifier.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/rend/acl.rb', line 177 def remove_role!(role) role_registry.remove!(role) role_id = (role.class <= Rend::Acl::Role) ? role.id : role @_rules[:all_resources][:by_role_id].each do |role_id_current, rules| if role_id == role_id_current @_rules[:all_resources][:by_role_id].delete(role_id_current) end end @_rules[:by_resource_id].each do |resource_id_current, visitor| if visitor.has_key?(:by_role_id) visitor[:by_role_id].each do |role_id_current, rules| if role_id == role_id_current @_rules[:by_resource_id][resource_id_current][:by_role_id].delete(role_id_current) end end end end self end |
#remove_role_all! ⇒ Object
Removes all Roles from the registry
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/rend/acl.rb', line 205 def remove_role_all! role_registry.remove_all! @_rules[:all_resources][:by_role_id].each do |role_id_current, rules| @_rules[:all_resources][:by_role_id].delete(role_id_current) end @_rules[:by_resource_id].each do |resource_id_current, visitor| visitor[:by_role_id].each do |role_id_current, rules| @_rules[:by_resource_id][resource_id_current][:by_role_id].delete(role_id_current) end end self end |
#resource!(resource) ⇒ Object
Returns the identified Resource
The resource parameter can either be a Resource or a Resource identifier.
262 263 264 265 266 |
# File 'lib/rend/acl.rb', line 262 def resource!(resource) resource_id = (resource.class <= Rend::Acl::Resource) ? resource.id : resource.to_s raise Rend::Acl::Exception, "Resource 'resource_id' not found" unless resource?(resource) @_resources[resource_id][:instance] end |
#resource?(resource) ⇒ Boolean
Returns true if and only if the Resource exists in the ACL
The resource parameter can either be a Resource or a Resource identifier.
274 275 276 277 |
# File 'lib/rend/acl.rb', line 274 def resource?(resource) resource_id = (resource.class <= Rend::Acl::Resource) ? resource.id : resource.to_s @_resources.keys.include?(resource_id) end |
#resources ⇒ Object
Returns array of registered resources.
642 643 644 |
# File 'lib/rend/acl.rb', line 642 def resources @_resources.keys end |
#role!(role) ⇒ Object
Returns the identified Role
The role parameter can either be a Role or Role identifier.
138 139 140 |
# File 'lib/rend/acl.rb', line 138 def role!(role) role_registry.get!(role) end |
#role?(role) ⇒ Boolean
Returns true if and only if the Role exists in the registry
The role parameter can either be a Role or a Role identifier.
149 150 151 |
# File 'lib/rend/acl.rb', line 149 def role?(role) role_registry.has?(role) end |
#role_registry ⇒ Object
Returns the Role registry for this ACL
If no Role registry has been created yet, a new default Role registry is created and returned.
627 628 629 |
# File 'lib/rend/acl.rb', line 627 def role_registry @_role_registry ||= Rend::Acl::Role::Registry.new end |
#roles ⇒ Object
Returns an array of registered roles.
Note that this method does not return instances of registered roles, but only the role identifiers.
637 638 639 |
# File 'lib/rend/acl.rb', line 637 def roles role_registry.roles.keys end |
#set_rule!(operation, type, roles = nil, resources = nil, privileges = nil, assertion = nil) ⇒ Object
Performs operations on ACL rules
The operation parameter may be either OP_ADD or OP_REMOVE, depending on whether the user wants to add or remove a rule, respectively:
OP_ADD specifics:
A rule is added that would allow one or more Roles access to [certain privileges
upon] the specified Resource(s).
OP_REMOVE specifics:
The rule is removed only in the context of the given Roles, Resources, and privileges.
Existing rules to which the remove operation does not apply would remain in the
ACL.
The type parameter may be either TYPE_ALLOW or TYPE_DENY, depending on whether the rule is intended to allow or deny permission, respectively.
The roles and resources parameters may be references to, or the string identifiers for, existing Resources/Roles, or they may be passed as arrays of these - mixing string identifiers and objects is ok - to indicate the Resources and Roles to which the rule applies. If either roles or resources is nil, then the rule applies to all Roles or all Resources, respectively. Both may be nil in order to work with the default rule of the ACL.
The privileges parameter may be used to further specify that the rule applies only to certain privileges upon the Resource(s) in question. This may be specified to be a single privilege with a string, and multiple privileges may be specified as an array of strings.
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
# File 'lib/rend/acl.rb', line 481 def set_rule!(operation, type, roles = nil, resources = nil, privileges = nil, assertion = nil) type_hint! Rend::Acl::Assertion, assertion # ensure that the rule type is valid normalize input to uppercase if type != TYPE_ALLOW && type != TYPE_DENY raise Rend::Acl::Exception, "Unsupported rule type must be either '#{TYPE_ALLOW}' or '#{TYPE_DENY}'" end # ensure that all specified Roles exist normalize input to array of Role objects or nil roles = Array(roles) roles << nil if roles.empty? roles = roles.reduce([]) {|seed, role| seed << (role ? role_registry.get!(role) : nil)} # ensure that all specified Resources exist normalize input to array of Resource objects or nil if resources resources = Array(resources) resources << nil if resources.empty? resources = resources.reduce([]) {|seed, resource| seed << (resource ? resource!(resource) : nil)} end # normalize privileges to array privileges = Array(privileges).compact case operation when OP_ADD then _add_rule!(type, roles, resources, privileges, assertion) when OP_REMOVE then _remove_rule!(type, roles, resources, privileges, assertion) else raise Rend::Acl::Exception, "Unsupported operation must be either '#{OP_ADD}' or '#{OP_REMOVE}'" end self end |