require_relative "../prepositions" module Acl9 module Dsl class Base include Prepositions attr_reader :allows, :denys def initialize(*args) @default_action = nil @allows = [] @denys = [] @original_args = args @action_clause = nil end def acl_block!(&acl_block) instance_eval(&acl_block) end def default_action @default_action.nil? ? :deny : @default_action end def allowance_expression allowed_expr = @allows.any? ? @allows.map { |clause| "(#{clause})" }.join(' || ') : 'false' not_denied_expr = @denys.any? ? @denys.map { |clause| "!(#{clause})" }.join(' && ') : 'true' [allowed_expr, not_denied_expr]. map { |expr| "(#{expr})" }. join(default_action == :deny ? ' && ' : ' || ') end alias to_s allowance_expression protected def default(default_action) raise ArgumentError, "default can only be called once in access_control block" if @default_action unless [:allow, :deny].include? default_action raise ArgumentError, "invalid value for default (can be :allow or :deny)" end @default_action = default_action end def allow(*args) @current_rule = :allow _parse_and_add_rule(*args) end def deny(*args) @current_rule = :deny _parse_and_add_rule(*args) end def actions(*args, &block) raise ArgumentError, "actions should receive at least 1 action as argument" if args.size < 1 subsidiary = self.class.new(*@original_args) class < 0 @denys << squash.call(subsidiary.denys) if subsidiary.denys.size > 0 end alias action actions def logged_in; false end def anonymous; nil end def all; true end alias everyone all alias everybody all alias anyone all def _permitted_allow_deny_option!(key) raise ArgumentError, "#{key} is not a valid option" unless [:to, :only, :except, :if, :unless, *VALID_PREPOSITIONS].include?(key.to_sym) end def _retrieve_only options only = [ options.delete(:only) ].flatten.compact only |= [ options.delete(:to) ].flatten.compact only if only.present? end def _parse_and_add_rule(*args) options = args.extract_options! options.keys.each { |key| _permitted_allow_deny_option!(key) } _set_action_clause( _retrieve_only(options), options.delete(:except)) object_s = _role_object_s(options) role_checks = args.map do |who| case who when anonymous then "#{_subject_ref}.nil?" when logged_in then "!#{_subject_ref}.nil?" when all then "true" else "!#{_subject_ref}.nil? && #{_subject_ref}.has_role?('#{who}'#{object_s})" end end [:if, :unless].each do |cond| val = options[cond] raise ArgumentError, "#{cond} option must be a Symbol" if val && !val.is_a?(Symbol) end condition = [ (_method_ref(options[:if]) if options[:if]), ("!#{_method_ref(options[:unless])}" if options[:unless]) ].compact.join(' && ') condition = nil if condition.blank? _add_rule(case role_checks.size when 0 raise ArgumentError, "allow/deny should have at least 1 argument" when 1 then role_checks.first else _either_of(role_checks) end, condition) end def _either_of(exprs) clause = exprs.map { |expr| "(#{expr})" }.join(' || ') return "(#{clause})" end def _add_rule(what, condition) anded = [what] + [@action_clause, condition].compact anded[0] = "(#{anded[0]})" if anded.size > 1 (@current_rule == :allow ? @allows : @denys) << anded.join(' && ') end def _set_action_clause(only, except) raise ArgumentError, "both :only (:to) and :except cannot be specified in the rule" if only && except @action_clause = nil action_list = only || except return unless action_list expr = _action_check_expression(action_list) @action_clause = only ? "#{expr}" : "!#{expr}" end def _action_check_expression(action_list) unless action_list.is_a?(Array) action_list = [ action_list.to_s ] end case action_list.size when 0 then "true" when 1 then "(#{_action_ref} == '#{action_list.first}')" else set_of_actions = "Set.new([" + action_list.map { |act| "'#{act}'"}.join(',') + "])" "#{set_of_actions}.include?(#{_action_ref})" end end def _role_object_s(options) object = _by_preposition options case object when Class then ", #{object}" when Symbol then ", #{_object_ref object}" when nil then "" else raise ArgumentError, "object specified by preposition can only be a Class or a Symbol" end end def _subject_ref raise end def _object_ref(object) raise end def _action_ref raise end def _method_ref(method) raise end end end end