cancancan authorize_resource not working as expected
Asked Answered
M

3

7

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.

Midshipman answered 11/7, 2017 at 15:7 Comment(0)
C
10

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.

Calabro answered 11/7, 2017 at 15:36 Comment(2)
I was not using the load_and_authorize_resource since I was prefetching when loading the resource. +1 for indicating how to get around this.Glissade
In my case solution was just to swap before_action :fetch_user with authorize_resource to load user first before authorization attempt.Accredit
P
2

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
Peterec answered 11/7, 2017 at 15:13 Comment(0)
E
0

When authorize_resource is used at the top of the controller it's actually setting a before_action that will complete the authorization check before the controller action, therefore the instance variable must be set before the controller action (not inside it), i.e. you must use a before_action to set the instance variable.

Example

class PostsController < ApplicationController

authorize_resource

# rest of controller

The before_action that authorize_resource creates will look for @post instance variable (or @posts for the index action) that must be created in a before_action (not in the controller action, as the authorization check will already have been run the time the controller action runs).

Resources

The info is from the cancancan source code / documentation for the authorize_resource method:

Sets up a before filter which authorizes the resource using the instance variable.For example, if you have an ArticlesController it will check the @article instance variable and ensure the user can perform the current action on it. Under the hood it is doing something like the following:

authorize!(params[:action].to_sym, @article || Article)

Elnaelnar answered 19/8, 2023 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.