lib/ixtlan/guard/guard_rails.rb in ixtlan-guard-0.7.2 vs lib/ixtlan/guard/guard_rails.rb in ixtlan-guard-0.8.0

- old
+ new

@@ -1,75 +1,221 @@ module Ixtlan - module ActionController #:nodoc: - module Guard #:nodoc: + module Guard + module ActionController def self.included(base) base.send(:include, InstanceMethods) base.send(:include, GroupsMethod) end + class Filter + attr_reader :block + def initialize(method, options, &block) + @only = options[:only] + @except = options[:except] || [] + @reference = options[:reference] + @reference = @reference.to_sym if @reference + @block = block + @method = method.to_sym if method + raise "illegal arguments: either block or method name #{method}" if block && method + end + + def proc(base, reference = nil) + if @block + if reference + Proc.new do |groups| + @block.call(groups, reference) + end + elsif @reference + Proc.new do |groups| + @block.call(groups, base.send(@reference)) + end + else + @block + end + else + if base.respond_to?(@method) || base.private_methods.include?(@method.to_s) + if reference + Proc.new do |groups| + base.send(@method, groups, reference) + end + elsif @reference + Proc.new do |groups| + base.send(@method, groups, base.send(@reference)) + end + else + base.method(@method) + end + else + if @reference + Proc.new do |groups| + base.class.send(@method, groups, base.send(@reference)) + end + else + base.class.method(@method) + end + end + end + end + + def allowed?(action) + action = action.to_sym + (@only && @only.member?(action)) || + ((@only.nil? || @only.empty?) && ! @except.member?(action)) + end + end + module GroupsMethod protected - def groups_for_current_user - if respond_to?(:current_user) && current_user - current_user.groups - else - [] - end + def current_groups + @current_groups ||= + if respond_to?(:current_user) && current_user + current_user.groups + else + [] + end end end module RootGroup protected - def groups_for_current_user + def current_groups ['root'] end end module InstanceMethods #:nodoc: + def self.included(base) + base.class_eval do + + def self.guard_filter(*args, &block) + method = nil#"_intern_#{guard_filters.size}" + options = {} + case args.size + when 1 + if args[0].is_a? Symbol + method = args[0] + else + options = args[0] + end + when 2 + method = args[0].to_sym + options = args[1] + else + raise "argument error, expected (Symbol, Hash) or (Symbol) or (Hash)" + end + + guard_filters << Filter.new(method, options, &block) + end + + def self.guard_filters + @_guard_filters ||= [] + end + + def self.guard + Rails.application.config.guard + end + + def self.allowed?(action, current_groups, reference = nil) + filter = guard_filters.detect do |f| + f.allowed?(action) + end + if filter + guard.check(self.controller_name, + action, + current_groups || []) do |groups| + filter.proc(self, reference).call(groups) + end + else + # TODO maybe do something with the reference + guard.check(controller_name, + action, + current_groups || []) + end + end + end + end + protected def guard - Rails.application.config.guard + self.class.guard end - def check(association = nil, &block) - group_method = respond_to?(:current_user_groups) ? :current_user_groups : :groups_for_current_user - unless guard.allowed?(params[:controller], - params[:action], - send(group_method), - association, - &block) - if association - raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}##{association.class}(#{association.id})'") - else - raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'") + def allowed?(action_or_actions, + controller = params[:controller], + reference = nil, + &block) + case action_or_actions + when Array + action_or_actions.detect do |action| + check(action, controller, reference, &block) != nil end + else + check(action_or_actions, controller, reference, &block) != nil end - true end - alias :authorize :check - def authorization - warn "DEPRECATED: use 'authorize' instead" - check + def check(action = params[:action], + controller = params[:controller], + reference = nil, + &block) + unless block + filter = self.class.guard_filters.detect do |f| + f.allowed?(action) + end + end + + if filter + guard.check(controller, + action, + current_groups || []) do |groups| + filter.proc(self).call(groups) + end + else + # TODO maybe do something with the reference + guard.check(controller, + action, + current_groups || [], + &block) + end end + + def authorize + unless check + raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'") + end + true + end end end - end - module Allowed #:nodoc: - # Inclusion hook to make #allowed available as method - def self.included(base) - base.send(:include, InstanceMethods) - end + module Allowed + # Inclusion hook to make #allowed available as method + def self.included(base) + base.send(:include, InstanceMethods) + end - module InstanceMethods #:nodoc: - def allowed?(resource, action) - controller.send(:guard).allowed?(resource, action, controller.send(:groups_for_current_user)) + module InstanceMethods #:nodoc: + def allowed?(resource, action, reference = nil) + if resource.to_s != controller.class.controller_name || reference + other = "#{resource}Controller".classify.constantize + if other.respond_to?(:allowed?) + if reference + other.send(:allowed?, action, controller.current_groups, reference) + else + other.send(:allowed?, action, controller.current_groups) + end + else + raise "can not find 'allowed?' on #{other}" + end + else + controller.send(:allowed?, action, resource) + end + end end end end end