Rails授权CanCanCan

Rails authorization CanCanCan

我正在尝试使用 CanCanCan 对路由实施一些授权 gem 但对于某些路由,它不起作用,它要么始终授权,要么根本不授权。

我只希望角色 ID 为 5 (admin) 的用户访问价格控制器的更新操作,这是我的 ability.rb 代码:

class Ability
  include CanCan::Ability

  def initialize(user)

    user ||= User.new
    # Define abilities for the passed in user here. For example:
    if user.present?
      can :show, User

      if user.role.id == 5
        can [:index, :update, :create], User
      end

      can :update, PricesController if user.role.id == 3
    #
    # The first argument to `can` is the action you are giving the user
    # permission to do.
    # If you pass :manage it will apply to every action. Other common actions
    # here are :read, :create, :update and :destroy.
    #
    # The second argument is the resource the user can perform the action on.
    # If you pass :all it will apply to every resource. Otherwise pass a Ruby
    # class of the resource.
    #
    # The third argument is an optional hash of conditions to further filter the
    # objects.
    # For example, here the user can only update published articles.
    #
    #   can :update, Article, :published => true
    end
  end
end

索引等的第一个动作工作正常,第二个动作,我调试了角色 ID 也被正确找到。所以故障必须在我的控制器中,这是我的代码:

def update
    authorize! :update, current_user

    if @prices.where(description: params[:description]).update(price_params)
      respond_to do |format|
        format.html { redirect_to prices_path }
        format.json { render json: @prices }
      end
    end  
  end

如果我使用 current_user 来检查授权方法,每个人都可以更改值,如果我使用 @prices 的实例变量,那么没有人可以执行控制器操作。

我也在处理异常:

rescue_from CanCan::AccessDenied do |e|
    respond_to do |format|
      format.html { redirect_to current_user, flash: { alert: "Sie besitzen dafür keine Berechtigung!" } }
      format.json { render json: { success: false }, status: 401 }
    end
  end

看了一遍又一遍的文档,还是想不出我的错在哪里。

一些不同的评论:

在你的 ability.rb 我会说使用

if user.role.name == 'admin'

而不是

if user.role.id == 5

因为除非您手动设置您的 ID,否则您可能必须更改它以用于生产。

还有

can :update, PricesController if user.role.id == 3

应该是

can :update, Price if user.role.id == 3

并在您的控制器中替换

authorize! :update, current_user

authorize! :update, Price

通常在 rails 更新操作中,您将只更新一个对象,并使用以下方式授权它:

authorize! :update, @price

但在你的情况下,我猜想通过模型授权是你的最佳途径。

能力定义应该是:

can :update, Price if user.role.id == 3

您授权模型 - 而不是控制器。

当调用授权时,你应该传递你正在更新的资源实例或者 class 如果没有实例:

authorize! :read, @thing
authorize! :index, Thing

但是控制器本身以一种与 CanCanCan 无关的方式从根本上被破坏了。这就是一切分崩离析的地方:

@prices.where(description: params[:description]).update(price_params)

where returns 一组记录 - #update 是一种在 单个记录 上调用的方法。我不知道这是否是一次非常幼稚且失败的大规模更新尝试,或者您是否正在尝试做类似 slug 列(漂亮的 url)的事情。但是你可能应该坚持 rails 约定,直到你知道自己在做什么:

def update
  @price = Price.find(params[:id])
  authorize!(:update, @price)
  if @price.update
    # ...
  else
    # ...
  end
end

在这种情况下您也可以只使用load_and_authorize_resource而不是手动查找和授权。