Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cancancan authorize_resource not working as expected

I am getting an unexpected behaviour for a simple cancancan authorization.

ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    # Define abilities for the passed in user here. For example:
    #
    user ||= User.new # guest user (not logged in)
    if user.is_admin?
        can :manage, :all
    elsif user.is_standard?
        can :manage, ServiceOrder, {user_id: user.id}
        can :manage, ServiceOrderDetail, :service_order => { :user_id => user.id }
    end

service_order.rb controller (partially shown)

class ServiceOrdersController < ApplicationController
  authorize_resource

  def show
    @service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
  end

end

This does not work, as it lets the controller show ANY service_order record, instead of just those owned by the current_user.

The only way that this works is if I manually authorize the controller adding:

authorize! :show, @service_order

like this:

  def show
    @service_order = ServiceOrder.includes(:service_order_details).find(params[:id])
    authorize! :show, @service_order
  end

which makes no sense since authorize_resource is supposed to be doing that.

like image 458
Augusto Samamé Barrientos Avatar asked Jul 11 '17 15:07

Augusto Samamé Barrientos


2 Answers

What is happening is the authorize_resource is happening before the show action, and since the @service_order is not set yet, it is checking against the class, and the user does have access to show a ServiceOrder just under constraint.

Adding authorize_resource will install a before_action callback that calls authorize!, passing the resource instance variable if it exists. If the instance variable isn't set (such as in the index action) it will pass in the class name. For example, if we have a ProductsController it will do this before each action.

authorize!(params[:action].to_sym, @product || Product)

from Cancancan documentations

What you will need to do is load_and_authorize_resource as suggested by widjajayd. or (if you do not want to use the cancancan default load action) do a before_filter that loads the resource manually using your custom method before the authorize_resource call.

like image 111
Michael Gorman Avatar answered Nov 12 '22 08:11

Michael Gorman


my suggestion: instead using authorize_resource you using load_and_authorize_resource, and below is the sample for your controller just make sure your strong_parameters declaration :service_order_params

  load_and_authorize_resource param_method: :service_order_params
like image 21
widjajayd Avatar answered Nov 12 '22 08:11

widjajayd