CanCan explanation of load_and_authorize_resource

21k views Asked by At

I would know how the load_and_authorize_resource works inside. I searched the github page Link and tried to undestand , but i didn't find nothing usefull. I only understand that load_and_authorize_resource is like a before_filter and it loads (in some way) the ability that we have written in ability.rb

I would know better how this is possible. I mean, i don't want to study ALL the gem, but i want just to see how cancan load the ability of a resource in a controller and if the load_and_authorize_resource is really a sort of before_filter.

1

There are 1 answers

2
twonegatives On BEST ANSWER

disclaimer: for the sake of simplicity, I omit some calls to short inner methods intentionally. The full chain of calling can be obtained by following load_and_authorize_resource method definition and so forth.

As stated in documentation, load_and_authorize_resource sets up a before_filter...

# cancan/lib/cancan/controller_additions.rb
def load_and_authorize_resource(*args)
  cancan_resource_class.add_before_filter(self, :load_and_authorize_resource, *args)
end

...which calls two methods: load_resource and authorize_resource.

# cancan/lib/cancan/controller_resource.rb
def load_and_authorize_resource
  load_resource
  authorize_resource
end

To get the idea of their behaviour we're going to look at both of them closely.

Based on params hash which was passed to your controller action, load_resource makes a decision on whether it should obtain a new instance of a class (e.g. Post.new) or find a particular instance based on params[:id] (e.g. Post.find(params[:id])). That instance (or a collection of instances for actions like index) is assigned to corresponding instance variable of your controller action.

# cancan/lib/cancan/controller_resource.rb
def load_resource
  unless skip?(:load)
    if load_instance?
      # here you have obtained your object, e.g. Post with id=5
      # and placed it into cancan resource_instance variable.
      # it has automatically set up @post instance variable for you
      # in your action
      self.resource_instance ||= load_resource_instance
    elsif load_collection?
      self.collection_instance ||= load_collection
    end
  end
end

Later on, authorize_resource gets called. Its inner logics syntax should be familiar to you: checking abilities by hands looks just the same as what happens inside of this method. Basically you take a resource_instance obtained at the previous step, params[:action] which is the name of a current action, and check if particular action can be accessed for given object(s).

# cancan/lib/cancan/controller_resource.rb
def authorize_resource
  unless skip?(:authorize)
    # similar to what happens when you call authorize!(:show, @post)
    @controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
  end
end

As long as raising exceptions inside of before_filter stops controller action from being executed, failing to pass authorization here gets you redirected to your application's home url, shown 500 error page or whatever behaviour you defined for CanCan::AccessDenied handling.

On the other hand, in case you've passed authorization successfully, your action code gets executed. Now you've got access to instance variable (e.g. @post) which has been set up by CanCan at load_resource step.