class Acu::Monitor
Attributes
kwargs[R]
Public Class Methods
args(kwargs)
click to toggle source
# File lib/acu/monitor.rb, line 14 def args kwargs @kwargs = @kwargs.merge(kwargs) end
clear_args()
click to toggle source
# File lib/acu/monitor.rb, line 18 def clear_args @kwargs = { } end
clear_cache()
click to toggle source
# File lib/acu/monitor.rb, line 126 def clear_cache return if not Configs.get :use_cache Rails.cache.clear namespace: (Configs.get :cache_namespace) end
gaurd(by: { })
click to toggle source
# File lib/acu/monitor.rb, line 22 def gaurd by: { } # assign the args in class scope args by # fetch the request & process it _info = process Acu::Listeners.data[:request] # return if we hit the cache return if hit_cache _info rules = Rules.rules.select do |cond, _| flag = true; # check if this is a global rule! next true if cond.empty? {namespace: nil, controller: :namespace, action: :controller}.each do |current, parent| t = -1 # either mentioned explicitly if cond[current] and not cond[current].empty? # hierarchical match `_info[current]` with `cond[current]` to support nested namespace (since v3.0.0) cond[current].map { |c| c[:name].to_s }.each.with_index do |c, index| t = (c == eval("_info.#{current}")[index].to_s) ? 1 : 0 break if t == 0 end # or in `only|except` tags elsif parent and cond[parent] and not cond[parent].empty? # if nothing mentioned in parent, assume for all t = 1 if not cond[parent].map { |c| c[:only] or c[:except] }.all? # flag true if it checked in namespace's only tag {only: {on_true: 1, on_false: 0} , except: {on_true: 0, on_false: 1}}.each do |tag, val| # fetch all `tag` names tag_list = cond[parent].map { |c| c[tag] }.flatten - [nil] # if any tag is present? if not tag_list.empty? and tag_list.any? # if `current` is mentioned in `tag_list`? case not (tag_list.map(&:to_s) & eval("_info.#{current}").map(&:to_s)).empty? when true t = val[:on_true] break when false t = val[:on_false] end end end end flag &= (t == 1) if t.between? 0, 1; break if not flag end flag end # flag so we can process all the related rule and it all passed this should be false # if any failed, and exception will be raised _granted = -1 _entitled_entities = []; # for each mached rule rules.each do |_, rule| # for each entity and it's actions in the rule rule.each do |entity, action| # check it the current request can relay to the entity? if valid_for? entity _entitled_entities << entity.to_s # current entity is granted to have the access? if is_allowed? action # cache the permision for the entity cache_access _info, _entitled_entities[-1], Rules.GRANT_SYMBOL # grant the access if already not denied _granted = 1 if _granted == -1 else # cache the permision for the entity cache_access _info, _entitled_entities[-1], Rules.DENY_SYMBOL # deny it, period! _granted = 0 end end end end # if the access is granted? i.e if all the rules are satisfied with the request return if _granted == 1 and access_granted _info, _entitled_entities # if the access is denied? i.e at least one of rules are NOT satisfied with the request return if _granted == 0 and access_denied _info, _entitled_entities # if we reached here it measn that have found no rule to deny/allow the request and we have to fallback to the defaults access_denied _info, [:__ACU_BY_DEFAULT__], by_default: true if not Configs.get :allow_by_default access_granted _info, [:__ACU_BY_DEFAULT__], by_default: true end
valid_for?(entity, **args)
click to toggle source
# File lib/acu/monitor.rb, line 109 def valid_for? entity, **args # check for existance raise Errors::MissingEntity.new("whois(:#{entity})?") if not Rules.entities[entity] # fetch the entity's identity e = Rules.entities[entity] # set default argument set wargs = @kwargs # set externals if any argument is provided from outside? wargs = args unless args.blank? # fetch the related args to the entity from the `kwargs` kwargs = wargs.reject { |x| !e[:args].include?(x) } # if fetched args and pre-defined arg didn't match? raise Errors::MissingData.new("at least one of arguments for `whois(:#{entity})` is not provided!") if kwargs.length != e[:args].length # send varibles in order the have defined e[:callback].call(*e[:args].map { |i| kwargs[i] }) end
Protected Class Methods
access_denied(_info, entities, by_default: false, from_cache: false)
click to toggle source
# File lib/acu/monitor.rb, line 204 def access_denied _info, entities, by_default: false, from_cache: false # log the event log_audit ("[x]" + (from_cache ? '[c]' : '') + " access DENIED to `#{_info}` as `:#{entities.uniq.sort.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : "")) # deny the access raise Errors::AccessDenied.new("you don't have the enough access for process this request!") end
access_granted(_info, entities, by_default: false, from_cache: false)
click to toggle source
# File lib/acu/monitor.rb, line 197 def access_granted _info, entities, by_default: false, from_cache: false # log the event log_audit ("[-]" + (from_cache ? '[c]' : '') + " access GRANTED to `#{_info}` as `:#{entities.uniq.sort.join(", :")}`" + (by_default ? " [autherized by :allow_by_default]" : "")) # grant the access true end
cache_access(_info, entities, symbol)
click to toggle source
# File lib/acu/monitor.rb, line 184 def cache_access _info, entities, symbol if not Rails.cache.exist?(cache_name(_info), cache_options) Rails.cache.write(cache_name(_info), { Rules.DENY_SYMBOL => [], Rules.GRANT_SYMBOL => [] }, cache_options) end cache_data = Rails.cache.read cache_name(_info), cache_options cache_data[symbol] += [entities].flatten.map(&:to_sym) cache_data[symbol] = cache_data[symbol].flatten.uniq Rails.cache.write cache_name(_info), cache_data, cache_options end
cache_name(_info, entities = [])
click to toggle source
# File lib/acu/monitor.rb, line 154 def cache_name _info, entities = [] ("%s-%s" %[_info.to_a.join('::'), (entities.kind_of?(Array) ? entities : entities.keys).sort.join("-")]).gsub(/-+$/, "") end
cache_options()
click to toggle source
# File lib/acu/monitor.rb, line 170 def cache_options out = { } # fetch cache options from config [:namespace, :expires_in, :race_condition_ttl].each { |k| out[k] = Configs.get "cache_#{k}".to_sym } return out end
hit_cache(_info)
click to toggle source
# File lib/acu/monitor.rb, line 133 def hit_cache _info # return [didn't hit] if not allowed to use cache return false if not Configs.get :use_cache # fetched cached data for current info cached_data = Rails.cache.read cache_name(_info), cache_options # return not hit if no cached data found return false if not cached_data # fetch the relative entities to this request _entitled_entities = Rules.entities.select { |name, _| valid_for? name }.keys.map(&:to_sym) # check if any of entities is among the should-denied ones? denied = cached_data[Rules.DENY_SYMBOL] & _entitled_entities # check if any of entities is among the should-grant ones? granted = cached_data[Rules.GRANT_SYMBOL] & _entitled_entities # check if we have any resons to deny the access? return true if not denied.empty? and access_denied _info, denied, from_cache: true # o.w. grant the access if any explicit rule return true if not granted.empty? and access_granted _info, granted, from_cache: true # if not granted nor denied by cache, discard the cache data & proceed return false end
is_allowed?(action)
click to toggle source
# File lib/acu/monitor.rb, line 158 def is_allowed? action case action when Rules.GRANT_SYMBOL return true when Rules.DENY_SYMBOL return false else log_audit "> access DENIED to undefined action as `:#{action}`" raise Exception.new("action `#{action}` is undefined!") end end
log_audit(log)
click to toggle source
# File lib/acu/monitor.rb, line 177 def log_audit log # fetch the log file from configuration file = Configs.get :audit_log_file # log if allowed? Logger.new(Configs.get :audit_log_file).info(log) if file and not file.blank? end
process(request)
click to toggle source
# File lib/acu/monitor.rb, line 211 def process request # validate the request parameters raise Errors::InvalidData.new("the request object needs to provided!") if not(request and request[:parameters]) # fetch the params p = request[:parameters] # try find the namespace/controller set nc = p["controller"].split('/'); # considering multi layer namespaces n = nc.length > 1 ? nc[0..-2] : nil c = nc.length > 1 ? nc.last : nc.first a = p["action"] # return it with structure Struct.new(:namespace, :controller, :action).new([n].flatten, [c].flatten, [a].flatten) end