lib/six.rb
class Six
class NoPackError < StandardError
def message
'No such pack'
end
end
class InvalidPackPassed < StandardError
def message
"Wrong Rule Pack. You must provide correct 'allowed' method"
end
end
class InitializeArgumentError < StandardError
def message
'Six.new require hash as pack argument in format {:name_of_pack => PackRules.new}'
end
end
attr_reader :rules_packs, :current_rule_pack
# Initialize ability object
#
# == Parameters:
# packs::
# A Hash or rules to add with initializtion
#
# == Returns:
# self
#
def initialize(packs = {})
raise InitializeArgumentError unless packs.is_a?(Hash)
@rules_packs = {}
@current_rule_pack = nil
packs.each { |key, pack| add_pack!(key, pack) }
end
# Set current pack from stored packs by key
#
# == Parameters:
# name::
# A Symbol declaring the key name of stored pack
#
# == Returns:
# self or false
#
def use_pack(name)
if pack_exist?(name)
@current_rule_pack = name.to_sym
self
end
end
# Same as use but raise exception if no pack found
def use_pack!(name)
use_pack(name) ? self : raise_no_such_pack
end
# Add pack to authorization class
#
# == Parameters:
# name::
# A Symbol declaring the key name of stored pack
# pack::
# Any kind of object responding to allowed method
#
# == Returns:
# true or false
#
def add_pack(name, pack)
rules_packs[name.to_sym] = pack if valid_rules_object?(pack)
end
# Same as add_pack but raise exception if pack is invalid
def add_pack!(name, pack)
add_pack(name, pack) || raise_incorrect_pack_object
end
# Add pack to authorization class w/o key
#
# == Parameters:
# pack::
# Any kind of object responding to allowed method
#
# == Returns:
# true or raise exception
#
def <<(pack)
add_pack!(pack.object_id.to_s, pack)
end
# Remove pack from authorization class
#
# == Parameters:
# name::
# A Symbol declaring the key name of stored pack
#
# == Returns:
# true or false
#
def remove_pack(name)
if pack_exist?(name)
@current_rule_pack = nil if rules_packs[name.to_sym] == @current_rule_pack
rules_packs.delete(name.to_sym)
end
end
# Same as remove_pack but raise exception if pack wasnt found
def remove_pack!(name)
remove_pack(name) || raise_no_such_pack
end
# Check if object for rule pack is valid
#
# == Parameters:
# pack::
# Any kind of object responding to allowed method
#
# == Returns:
# true or false
#
def valid_rules_object?(object)
object.respond_to?(:allowed)
end
# Check if authorization class has pack with such name
#
# == Parameters:
# name::
# A Symbol declaring the key name of stored pack
#
# == Returns:
# true or false
#
def pack_exist?(name)
rules_packs.has_key?(name.to_sym)
end
# Check if authorization class allow access for object to subject
# using selected pack or all stored.
# Basically this method
# 1. send :allowed for every stored object in packs and pass object & subject
# 2. check if any of results include allowed action
#
# == Parameters:
# action::
# Action name to check for access
# object::
# object trying to access resource
# subject::
# resource
#
# == Returns:
# true or false
#
def allowed?(object, actions, subject)
# if multiple actions passed
# check all actions to be allowed
if actions.respond_to?(:each)
actions.all? { |action| action_included?(object, action, subject) }
else
# single action check
action_included?(object, actions, subject)
end
end
# Reset current used rule pack so auth class use
# global allowed? for new request
def reset_use
@current_rule_pack = nil
end
protected
def action_included?(object, action, subject)
if current_rule_pack
rules_packs[current_rule_pack].allowed(object, subject).include?(action)
else
rules_packs.values.map { |rp| rp.allowed(object, subject) }.flatten.include?(action)
end
end
def raise_no_such_pack
raise Six::NoPackError
end
def raise_incorrect_pack_object
raise Six::InvalidPackPassed
end
# shotcuts for long methods
alias use use_pack
alias use! use_pack!
alias add add_pack
alias add! add_pack!
alias remove remove_pack
alias remove! remove_pack!
alias reset reset_use
alias exist? pack_exist?
end