module LucidPolicy module Mixin def self.included(base) base.instance_exec do if RUBY_ENGINE != 'opal' Isomorfeus.add_valid_policy_class(base) unless base == LucidPolicy::Base end def authorization_rules @authorization_rules ||= { rules: {}.dup, policies: {}.dup, others: :deny }.dup end def all :others end def allow(*classes_and_methods_and_options) _allow_or_deny(:allow, *classes_and_methods_and_options) end def deny(*classes_and_methods_and_options) _allow_or_deny(:deny, *classes_and_methods_and_options) end def others :others end def rule(*classes_and_methods, &block) _allow_or_deny(:rule, *classes_and_methods, &block) end def combine_with(policy_class, **options) authorization_rules[:policies] = { policy_class => options } end private def _allow_or_deny(thing, *classes_methods_options, &block) rules = authorization_rules if %i[allow deny].include?(thing) && classes_methods_options.first == :others rules[:others] = thing return end target_classes = [] target_methods = [] target_options = {} classes_methods_options.each do |class_method_option| if class_method_option.class == Hash target_options = class_method_option else class_or_method_s = class_method_option.to_s if class_method_option.class == Symbol && class_or_method_s[0].downcase == class_or_method_s[0] target_methods << class_method_option else class_method_option = class_method_option.to_s unless class_method_option.class == String target_classes << class_method_option end end end thing_or_block = block_given? ? block : thing target_classes.each do |target_class| rules[:rules][target_class] = {} unless rules[:rules].key?(target_class) if target_methods.empty? rules[:rules][target_class][:rule] = thing_or_block rules[:rules][target_class][:options] = target_options unless target_options.empty? else rules[:rules][target_class][:rule] = :deny unless rules[:rules][target_class].key?(:rule) rules[:rules][target_class][:methods] = {} unless rules[:rules][target_class].key?(:methods) target_methods.each do |target_method| rules[:rules][target_class][:methods][target_method] = { rule: thing_or_block } rules[:rules][target_class][:methods][target_method][:options] = target_options unless target_options.empty? end end end end end def initialize(object) @object = object end def authorized?(target_class, target_method = nil, props = nil) Isomorfeus.raise_error(error_class: LucidPolicy::Exception, message: "#{self}: At least the class must be given!") unless target_class target_class = target_class.to_s unless target_class.class == String rules = self.class.authorization_rules result = if rules[:rules].key?(target_class) if target_method && rules[:rules][target_class].key?(:methods) && rules[:rules][target_class][:methods].key?(target_method) options = rules[:rules][target_class][:methods][target_method][:options] rule = rules[:rules][target_class][:methods][target_method][:rule] else options = rules[:rules][target_class][:options] rule = rules[:rules][target_class][:rule] end if rule.class == Symbol if options condition, method_result = __get_condition_and_result(options) rule if (condition == :if && method_result == true) || (condition == :if_not && method_result == false) else rule end else props = LucidProps.new(props) unless props.class == LucidProps policy_helper = LucidPolicy::Helper.new policy_helper.instance_exec(@object, target_class, target_method, props, &rule) policy_helper.result end else rules[:others] end return true if result == :allow rules[:policies].each do |policy_class, options| combined_policy_result = nil if options.empty? combined_policy_result = policy_class.new(@object).authorized?(target_class, target_method, props) else condition, method_result = __get_condition_and_result(options) if (condition == :if && method_result == true) || (condition == :if_not && method_result == false) combined_policy_result = policy_class.new(@object).authorized?(target_class, target_method, props) end end return true if combined_policy_result == true end result == :allow ? true : false end def authorized!(target_class, target_method = nil, props = nil) return true if authorized?(target_class, target_method, props) Isomorfeus.raise_error(error_class: LucidPolicy::Exception, message: "#{@object}: not authorized to call #{target_class}.#{target_method}(#{props})!") end private def __get_condition_and_result(options) condition = nil method_name_or_block = if options.key?(:if) condition = :if options[:if] elsif options.key?(:if_not) condition = :if_not options[:if_not] elsif options.key?(:unless) condition = :if_not options[:unless] end method_result = if method_name_or_block && method_name_or_block.class == Symbol @object.__send__(method_name_or_block) else props = LucidProps.new(props) unless props.class == LucidProps method_name_or_block.call(@object, props) end [condition, method_result] end end end end