# Authorization::AuthorizationInController
require File.dirname(__FILE__) + '/authorization.rb'

module Authorization
  module AuthorizationInController

    def self.included(base) # :nodoc:
      base.extend(ClassMethods)
      base.hide_action :authorization_engine, :permitted_to?,
        :permitted_to!
    end

    DEFAULT_DENY = false

    # If attribute_check is set for filter_access_to, decl_auth_context will try to
    # load the appropriate object from the current controller's model with
    # the id from params[:id].  If that fails, a 404 Not Found is often the
    # right way to handle the error.  If you have additional measures in place
    # that restricts the find scope, handling this error as a permission denied
    # might be a better way.  Set failed_auto_loading_is_not_found to false
    # for the latter behavior.
    @@failed_auto_loading_is_not_found = true
    def self.failed_auto_loading_is_not_found?
      @@failed_auto_loading_is_not_found
    end
    def self.failed_auto_loading_is_not_found= (new_value)
      @@failed_auto_loading_is_not_found = new_value
    end

    # Returns the Authorization::Engine for the current controller.
    def authorization_engine
      @authorization_engine ||= Authorization::Engine.instance
    end

    # If the current user meets the given privilege, permitted_to? returns true
    # and yields to the optional block.  The attribute checks that are defined
    # in the authorization rules are only evaluated if an object is given
    # for context.
    #
    # See examples for Authorization::AuthorizationHelper #permitted_to?
    #
    # If no object or context is specified, the controller_name is used as
    # context.
    #
    def permitted_to? (privilege, object_or_sym = nil, options = {})
      if authorization_engine.permit!(privilege, options_for_permit(object_or_sym, options, false))
        yield if block_given?
        true
      else
        false
      end
    end

    # Works similar to the permitted_to? method, but
    # throws the authorization exceptions, just like Engine#permit!
    def permitted_to! (privilege, object_or_sym = nil, options = {})
      authorization_engine.permit!(privilege, options_for_permit(object_or_sym, options, true))
    end

    # While permitted_to? is used for authorization, in some cases
    # content should only be shown to some users without being concerned
    # with authorization.  E.g. to only show the most relevant menu options
    # to a certain group of users.  That is what has_role? should be used for.
    def has_role? (*roles, &block)
      user_roles = authorization_engine.roles_for(current_user)
      result = roles.all? do |role|
        user_roles.include?(role)
      end
      yield if result and block_given?
      result
    end

    # Intended to be used where you want to allow users with any single listed role to view
    # the content in question
    def has_any_role?(*roles,&block)
      user_roles = authorization_engine.roles_for(current_user)
      result = roles.any? do |role|
        user_roles.include?(role)
      end
      yield if result and block_given?
      result
    end

    # As has_role? except checks all roles included in the role hierarchy
    def has_role_with_hierarchy?(*roles, &block)
      user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
      result = roles.all? do |role|
        user_roles.include?(role)
      end
      yield if result and block_given?
      result
    end

    # As has_any_role? except checks all roles included in the role hierarchy
    def has_any_role_with_hierarchy?(*roles, &block)
      user_roles = authorization_engine.roles_with_hierarchy_for(current_user)
      result = roles.any? do |role|
        user_roles.include?(role)
      end
      yield if result and block_given?
      result
    end

    protected
    def filter_access_filter # :nodoc:
      permissions = self.class.all_filter_access_permissions
      all_permissions = permissions.select {|p| p.actions.include?(:all)}
      matching_permissions = permissions.select {|p| p.matches?(action_name)}
      allowed = false
      auth_exception = nil
      begin
        allowed = if !matching_permissions.empty?
                    matching_permissions.all? {|perm| perm.permit!(self)}
                  elsif !all_permissions.empty?
                    all_permissions.all? {|perm| perm.permit!(self)}
                  else
                    !DEFAULT_DENY
                  end
      rescue NotAuthorized => e
        auth_exception = e
      end

      unless allowed
        if all_permissions.empty? and matching_permissions.empty?
          logger.warn "Permission denied: No matching filter access " +
            "rule found for #{self.class.controller_name}.#{action_name}"
        elsif auth_exception
          logger.info "Permission denied: #{auth_exception}"
        end
        if respond_to?(:permission_denied, true)
          # permission_denied needs to render or redirect
          send(:permission_denied)
        else
          send(:render, :text => "You are not allowed to access this action.",
            :status => :forbidden)
        end
      end
    end

    def load_controller_object (context_without_namespace = nil, model = nil) # :nodoc:
      instance_var = :"@#{context_without_namespace.to_s.singularize}"
      model = model ? model.classify.constantize : context_without_namespace.to_s.classify.constantize
      instance_variable_set(instance_var, model.find(params[:id]))
    end

    def load_parent_controller_object (parent_context_without_namespace) # :nodoc:
      instance_var = :"@#{parent_context_without_namespace.to_s.singularize}"
      model = parent_context_without_namespace.to_s.classify.constantize
      instance_variable_set(instance_var, model.find(params[:"#{parent_context_without_namespace.to_s.singularize}_id"]))
    end

    def new_controller_object_from_params (context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
      model_or_proxy = parent_context_without_namespace ?
           instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
           context_without_namespace.to_s.classify.constantize
      instance_var = :"@#{context_without_namespace.to_s.singularize}"
      instance_variable_set(instance_var,
        model_or_proxy.new(params[context_without_namespace.to_s.singularize]))
    end

    def new_blank_controller_object (context_without_namespace, parent_context_without_namespace, strong_params, model) # :nodoc:
      if model
        model_or_proxy = model.to_s.classify.constantize
      else
        model_or_proxy = parent_context_without_namespace ?
        instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
        context_without_namespace.to_s.classify.constantize
      end
      instance_var = :"@#{context_without_namespace.to_s.singularize}"
      instance_variable_set(instance_var,
        model_or_proxy.new())
    end

    def new_controller_object_for_collection (context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
      model_or_proxy = parent_context_without_namespace ?
           instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
           context_without_namespace.to_s.classify.constantize
      instance_var = :"@#{context_without_namespace.to_s.singularize}"
      instance_variable_set(instance_var, model_or_proxy.new)
    end

    def options_for_permit (object_or_sym = nil, options = {}, bang = true)
      context = object = nil
      if object_or_sym.nil?
        context = self.class.decl_auth_context
      elsif !Authorization.is_a_association_proxy?(object_or_sym) and object_or_sym.is_a?(Symbol)
        context = object_or_sym
      else
        object = object_or_sym
      end

      result = {:object => object,
        :context => context,
        :skip_attribute_test => object.nil?,
        :bang => bang}.merge(options)
      result[:user] = current_user unless result.key?(:user)
      result
    end

    module ClassMethods
      #
      # Defines a filter to be applied according to the authorization of the
      # current user.  Requires at least one symbol corresponding to an
      # action as parameter.  The special symbol :+all+ refers to all action.
      # The all :+all+ statement is only employed if no specific statement is
      # present.
      #   class UserController < ApplicationController
      #     filter_access_to :index
      #     filter_access_to :new, :edit
      #     filter_access_to :all
      #     ...
      #   end
      #
      # The default is to allow access unconditionally if no rule matches.
      # Thus, including the +filter_access_to+ :+all+ statement is a good
      # idea, implementing a default-deny policy.
      #
      # When the access is denied, the method +permission_denied+ is called
      # on the current controller, if defined.  Else, a simple "you are not
      # allowed" string is output.  Log.info is given more information on the
      # reasons of denial.
      #
      #   def permission_denied
      #     flash[:error] = 'Sorry, you are not allowed to the requested page.'
      #     respond_to do |format|
      #       format.html { redirect_to(:back) rescue redirect_to('/') }
      #       format.xml  { head :unauthorized }
      #       format.js   { head :unauthorized }
      #     end
      #   end
      #
      # By default, required privileges are inferred from the action name and
      # the controller name.  Thus, in UserController :+edit+ requires
      # :+edit+ +users+.  To specify required privilege, use the option :+require+
      #   filter_access_to :new, :create, :require => :create, :context => :users
      #
      # Without the :+attribute_check+ option, no constraints from the
      # authorization rules are enforced because for some actions (collections,
      # +new+, +create+), there is no object to evaluate conditions against.  To
      # allow attribute checks on all actions, it is a common pattern to provide
      # custom objects through +before_filters+:
      #   class BranchesController < ApplicationController
      #     before_filter :load_company
      #     before_filter :new_branch_from_company_and_params,
      #       :only => [:index, :new, :create]
      #     filter_access_to :all, :attribute_check => true
      #
      #     protected
      #     def new_branch_from_company_and_params
      #       @branch = @company.branches.new(params[:branch])
      #     end
      #   end
      # NOTE: +before_filters+ need to be defined before the first
      # +filter_access_to+ call.
      #
      # For further customization, a custom filter expression may be formulated
      # in a block, which is then evaluated in the context of the controller
      # on a matching request.  That is, for checking two objects, use the
      # following:
      #   filter_access_to :merge do
      #     permitted_to!(:update, User.find(params[:original_id])) and
      #       permitted_to!(:delete, User.find(params[:id]))
      #   end
      # The block should raise a Authorization::AuthorizationError or return
      # false if the access is to be denied.
      #
      # Later calls to filter_access_to with overlapping actions overwrite
      # previous ones for that action.
      #
      # All options:
      # [:+require+]
      #   Privilege required; defaults to action_name
      # [:+context+]
      #   The privilege's context, defaults to decl_auth_context, which consists
      #   of controller_name, prepended by any namespaces
      # [:+attribute_check+]
      #   Enables the check of attributes defined in the authorization rules.
      #   Defaults to false.  If enabled, filter_access_to will use a context
      #   object from one of the following sources (in that order):
      #   * the method from the :+load_method+ option,
      #   * an instance variable named after the singular of the context
      #     (by default from the controller name, e.g. @post for PostsController),
      #   * a find on the context model, using +params+[:id] as id value.
      #   Any of these methods will only be employed if :+attribute_check+
      #   is enabled.
      # [:+model+]
      #   The data model to load a context object from.  Defaults to the
      #   context, singularized.
      # [:+load_method+]
      #   Specify a method by symbol or a Proc object which should be used
      #   to load the object.  Both should return the loaded object.
      #   If a Proc object is given, e.g. by way of
      #   +lambda+, it is called in the instance of the controller.
      #   Example demonstrating the default behavior:
      #     filter_access_to :show, :attribute_check => true,
      #                      :load_method => lambda { User.find(params[:id]) }
      #

      def filter_access_to (*args, &filter_block)
        options = args.last.is_a?(Hash) ? args.pop : {}
        options = {
          :require => nil,
          :context => nil,
          :attribute_check => false,
          :model => nil,
          :load_method => nil,
          :strong_parameters => nil
        }.merge!(options)
        privilege = options[:require]
        context = options[:context]
        actions = args.flatten

        # prevent setting filter_access_filter multiple times
        skip_before_filter :filter_access_filter
        before_filter :filter_access_filter

        filter_access_permissions.each do |perm|
          perm.remove_actions(actions)
        end
        filter_access_permissions <<
          ControllerPermission.new(actions, privilege, context,
                                   options[:strong_parameters],
                                   options[:attribute_check],
                                   options[:model],
                                   options[:load_method],
                                   filter_block)
      end

      # Collecting all the ControllerPermission objects from the controller
      # hierarchy.  Permissions for actions are overwritten by calls to
      # filter_access_to in child controllers with the same action.
      def all_filter_access_permissions # :nodoc:
        ancestors.inject([]) do |perms, mod|
          if mod.respond_to?(:filter_access_permissions, true)
            perms +
              mod.filter_access_permissions.collect do |p1|
                p1.clone.remove_actions(perms.inject(Set.new) {|actions, p2| actions + p2.actions})
              end
          else
            perms
          end
        end
      end

      # To DRY up the filter_access_to statements in restful controllers,
      # filter_resource_access combines typical filter_access_to and
      # before_filter calls, which set up the instance variables.
      #
      # The simplest case are top-level resource controllers with only the
      # seven CRUD methods, e.g.
      #   class CompanyController < ApplicationController
      #     filter_resource_access
      #
      #     def index...
      #   end
      # Here, all CRUD actions are protected through a filter_access_to :all
      # statement.  :+attribute_check+ is enabled for all actions except for
      # the collection action :+index+.  To have an object for attribute checks
      # available, filter_resource_access will set the instance variable
      # @+company+ in before filters.  For the member actions (:+show+, :+edit+,
      # :+update+, :+destroy+) @company is set to Company.find(params[:id]).
      # For +new+ actions (:+new+, :+create+), filter_resource_access creates
      # a new object from company parameters: Company.new(params[:company].
      #
      # For nested resources, the parent object may be loaded automatically.
      #   class BranchController < ApplicationController
      #     filter_resource_access :nested_in => :companies
      #   end
      # Again, the CRUD actions are protected.  Now, for all CRUD actions,
      # the parent object @company is loaded from params[:company_id].  It is
      # also used when creating @branch for +new+ actions.  Here, attribute_check
      # is enabled for the collection :+index+ as well, checking attributes on a
      # @company.branches.new method.
      #
      # In many cases, the default seven CRUD actions are not sufficient.  As in
      # the resource definition for routing you may thus give additional member,
      # new and collection methods.  The +options+ allow you to specify the
      # required privileges for each action by providing a hash or an array of
      # pairs.  By default, for each action the action name is taken as privilege
      # (action search in the example below requires the privilege :index
      # :companies).  Any controller action that is not specified and does not
      # belong to the seven CRUD actions is handled as a member method.
      #   class CompanyController < ApplicationController
      #     filter_resource_access :collection => [[:search, :index], :index],
      #         :additional_member => {:mark_as_key_company => :update}
      #   end
      # The +additional_+* options add to the respective CRUD actions,
      # the other options (:+member+, :+collection+, :+new+) replace their
      # respective CRUD actions.
      #    filter_resource_access :member => { :toggle_open => :update }
      # Would declare :toggle_open as the only member action in the controller and
      # require that permission :update is granted for the current user.
      #    filter_resource_access :additional_member => { :toggle_open => :update }
      # Would add a member action :+toggle_open+ to the default members, such as :+show+.
      #
      # If :+collection+ is an array of method names filter_resource_access will
      # associate a permission with the method that is the same as the method
      # name and no attribute checks will be performed unless
      #   :attribute_check => true
      # is added in the options.
      #
      # You can override the default object loading by implementing any of the
      # following instance methods on the controller.  Examples are given for the
      # BranchController (with +nested_in+ set to :+companies+):
      # [+new_branch_from_params+]
      #   Used for +new+ actions.
      # [+new_branch_for_collection+]
      #   Used for +collection+ actions if the +nested_in+ option is set.
      # [+load_branch+]
      #   Used for +member+ actions.
      # [+load_company+]
      #   Used for all +new+, +member+, and +collection+ actions if the
      #   +nested_in+ option is set.
      #
      # All options:
      # [:+member+]
      #   Member methods are actions like +show+, which have an params[:id] from
      #   which to load the controller object and assign it to @controller_name,
      #   e.g. @+branch+.
      #
      #   By default, member actions are [:+show+, :+edit+, :+update+,
      #   :+destroy+].  Also, any action not belonging to the seven CRUD actions
      #   are handled as member actions.
      #
      #   There are three different syntax to specify member, collection and
      #   new actions.
      #   * Hash:  Lets you set the required privilege for each action:
      #     {:+show+ => :+show+, :+mark_as_important+ => :+update+}
      #   * Array of actions or pairs: [:+show+, [:+mark_as_important+, :+update+]],
      #     with single actions requiring the privilege of the same name as the method.
      #   * Single method symbol: :+show+
      # [:+additional_member+]
      #   Allows to add additional member actions to the default resource +member+
      #   actions.
      # [:+collection+]
      #   Collection actions are like :+index+, actions without any controller object
      #   to check attributes of.  If +nested_in+ is given, a new object is
      #   created from the parent object, e.g. @company.branches.new.  Without
      #   +nested_in+, attribute check is deactivated for these actions.  By
      #   default, collection is set to :+index+.
      # [:+additional_collection+]
      #   Allows to add additional collection actions to the default resource +collection+
      #   actions.
      # [:+new+]
      #   +new+ methods are actions such as +new+ and +create+, which don't
      #   receive a params[:id] to load an object from, but
      #   a params[:controller_name_singular] hash with attributes for a new
      #   object.  The attributes will be used here to create a new object and
      #   check the object against the authorization rules.  The object is
      #   assigned to @controller_name_singular, e.g. @branch.
      #
      #   If +nested_in+ is given, the new object
      #   is created from the parent_object.controller_name
      #   proxy, e.g. company.branches.new(params[:branch]).  By default,
      #   +new+ is set to [:new, :create].
      # [:+additional_new+]
      #   Allows to add additional new actions to the default resource +new+ actions.
      # [:+context+]
      #   The context is used to determine the model to load objects from for the
      #   before_filters and the context of privileges to use in authorization
      #   checks.
      # [:+nested_in+]
      #   Specifies the parent controller if the resource is nested in another
      #   one.  This is used to automatically load the parent object, e.g.
      #   @+company+ from params[:company_id] for a BranchController nested in
      #   a CompanyController.
      # [:+shallow+]
      #   Only relevant when used in conjunction with +nested_in+. Specifies a nested resource
      #   as being a shallow nested resource, resulting in the controller not attempting to
      #   load a parent object for all member actions defined by +member+ and
      #   +additional_member+ or rather the default member actions (:+show+, :+edit+,
      #   :+update+, :+destroy+).
      # [:+no_attribute_check+]
      #   Allows to set actions for which no attribute check should be performed.
      #   See filter_access_to on details.  By default, with no +nested_in+,
      #   +no_attribute_check+ is set to all collections.  If +nested_in+ is given
      #   +no_attribute_check+ is empty by default.
      # [:+strong_parameters+]
      #   If set to true, relies on controller to provide instance variable and
      #   create new object in :create action.  Set true if you use strong_params
      #   and false if you use protected_attributes.
      #
      def filter_resource_access(options = {})
        options = {
          :new        => [:new, :create],
          :additional_new => nil,
          :member     => [:show, :edit, :update, :destroy],
          :additional_member => nil,
          :collection => [:index],
          :additional_collection => nil,
          #:new_method_for_collection => nil,  # only symbol method name
          #:new_method => nil,                 # only symbol method name
          #:load_method => nil,                # only symbol method name
          :no_attribute_check => nil,
          :context    => nil,
          :model => nil,
          :nested_in  => nil,
          :strong_parameters => nil
        }.merge(options)
        options.merge!({ :strong_parameters => true }) if Rails.version >= '4' && options[:strong_parameters] == nil
        options.merge!({ :strong_parameters => false }) if Rails.version < '4' && options[:strong_parameters] == nil

        new_actions = actions_from_option( options[:new] ).merge(
            actions_from_option(options[:additional_new]) )
        members = actions_from_option(options[:member]).merge(
            actions_from_option(options[:additional_member]))
        collections = actions_from_option(options[:collection]).merge(
            actions_from_option(options[:additional_collection]))

        no_attribute_check_actions = options[:strong_parameters] ? collections.merge(actions_from_option([:create])) : collections

        options[:no_attribute_check] ||= no_attribute_check_actions.keys unless options[:nested_in]

        unless options[:nested_in].blank?
          load_parent_method = :"load_#{options[:nested_in].to_s.singularize}"
          shallow_exceptions = options[:shallow] ? {:except => members.keys} : {}
          before_filter shallow_exceptions do |controller|
            if controller.respond_to?(load_parent_method, true)
              controller.send(load_parent_method)
            else
              controller.send(:load_parent_controller_object, options[:nested_in])
            end
          end

          new_for_collection_method = :"new_#{controller_name.singularize}_for_collection"
          before_filter :only => collections.keys do |controller|
            # new_for_collection
            if controller.respond_to?(new_for_collection_method, true)
              controller.send(new_for_collection_method)
            else
              controller.send(:new_controller_object_for_collection,
                  options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
            end
          end
        end

        unless options[:strong_parameters]
          new_from_params_method = :"new_#{controller_name.singularize}_from_params"
          before_filter :only => new_actions.keys do |controller|
            # new_from_params
            if controller.respond_to?(new_from_params_method, true)
              controller.send(new_from_params_method)
            else
              controller.send(:new_controller_object_from_params,
                  options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
            end
          end
        else
          new_object_method = :"new_#{controller_name.singularize}"
          before_filter :only => :new do |controller|
            # new_from_params
            if controller.respond_to?(new_object_method, true)
              controller.send(new_object_method)
            else
              controller.send(:new_blank_controller_object,
                  options[:context] || controller_name, options[:nested_in], options[:strong_parameters], options[:model])
            end
          end
        end

        load_method = :"load_#{controller_name.singularize}"
        before_filter :only => members.keys do |controller|
          # load controller object
          if controller.respond_to?(load_method, true)
            controller.send(load_method)
          else
            controller.send(:load_controller_object, options[:context] || controller_name, options[:model])
          end
        end
        filter_access_to :all, :attribute_check => true, :context => options[:context], :model => options[:model]

        members.merge(new_actions).merge(collections).each do |action, privilege|
          if action != privilege or (options[:no_attribute_check] and options[:no_attribute_check].include?(action))
            filter_options = {
              :strong_parameters => options[:strong_parameters],
              :context          => options[:context],
              :attribute_check  => !options[:no_attribute_check] || !options[:no_attribute_check].include?(action),
              :model => options[:model]
            }
            filter_options[:require] = privilege if action != privilege
            filter_access_to(action, filter_options)
          end
        end
      end

      # Returns the context for authorization checks in the current controller.
      # Uses the controller_name and prepends any namespaces underscored and
      # joined with underscores.
      #
      # E.g.
      #   AllThosePeopleController         => :all_those_people
      #   AnyName::Space::ThingsController => :any_name_space_things
      #
      def decl_auth_context
        prefixes = name.split('::')[0..-2].map(&:underscore)
        ((prefixes + [controller_name]) * '_').to_sym
      end

      protected
      def filter_access_permissions # :nodoc:
        unless filter_access_permissions?
          ancestors[1..-1].reverse.each do |mod|
            mod.filter_access_permissions if mod.respond_to?(:filter_access_permissions, true)
          end
        end
        class_variable_set(:@@declarative_authorization_permissions, {}) unless filter_access_permissions?
        class_variable_get(:@@declarative_authorization_permissions)[self.name] ||= []
      end

      def filter_access_permissions? # :nodoc:
        class_variable_defined?(:@@declarative_authorization_permissions)
      end

      def actions_from_option (option) # :nodoc:
        case option
        when nil
          {}
        when Symbol, String
          {option.to_sym => option.to_sym}
        when Hash
          option
        when Enumerable
          option.each_with_object({}) do |action, hash|
            if action.is_a?(Array)
              raise "Unexpected option format: #{option.inspect}" if action.length != 2
              hash[action.first] = action.last
            else
              hash[action.to_sym] = action.to_sym
            end
          end
        end
      end
    end
  end

  class ControllerPermission # :nodoc:
    attr_reader :actions, :privilege, :context, :attribute_check, :strong_params
    def initialize (actions, privilege, context, strong_params, attribute_check = false,
                    load_object_model = nil, load_object_method = nil,
                    filter_block = nil)
      @actions = actions.to_set
      @privilege = privilege
      @context = context
      @load_object_model = load_object_model
      @load_object_method = load_object_method
      @filter_block = filter_block
      @attribute_check = attribute_check
      @strong_params = strong_params
    end

    def matches? (action_name)
      @actions.include?(action_name.to_sym)
    end

    def permit! (contr)
      if @filter_block
        return contr.instance_eval(&@filter_block)
      end
      object = @attribute_check ? load_object(contr) : nil
      privilege = @privilege || :"#{contr.action_name}"

      contr.authorization_engine.permit!(privilege,
                                         :user => contr.send(:current_user),
                                         :object => object,
                                         :skip_attribute_test => !@attribute_check,
                                         :context => @context || contr.class.decl_auth_context)
    end

    def remove_actions (actions)
      @actions -= actions
      self
    end

    private

    def load_object(contr)
      if @load_object_method and @load_object_method.is_a?(Symbol)
        contr.send(@load_object_method)
      elsif @load_object_method and @load_object_method.is_a?(Proc)
        contr.instance_eval(&@load_object_method)
      else
        load_object_model = @load_object_model ||
            (@context ? @context.to_s.classify.constantize : contr.class.controller_name.classify.constantize)
        load_object_model = load_object_model.classify.constantize if load_object_model.is_a?(String)
        instance_var = "@#{load_object_model.name.demodulize.underscore}"
        object = contr.instance_variable_get(instance_var)
        unless object
          begin
            object = @strong_params ? load_object_model.find_or_initialize_by(:id => contr.params[:id]) : load_object_model.find(contr.params[:id])
          rescue => e
            contr.logger.debug("filter_access_to tried to find " +
                "#{load_object_model} from params[:id] " +
                "(#{contr.params[:id].inspect}), because attribute_check is enabled " +
                "and #{instance_var.to_s} isn't set, but failed: #{e.class.name}: #{e}")
            raise if AuthorizationInController.failed_auto_loading_is_not_found?
          end
          contr.instance_variable_set(instance_var, object)
        end
        object
      end
    end
  end
end
