简体   繁体   English

Rails 授权 CanCanCan

[英]Rails authorization CanCanCan

I'm trying to implement some authorization to routes with the CanCanCan gem but for some routes, it won't work and it's either always authorizes no matter what or not authorized at all.我正在尝试使用 CanCanCan gem 实现对路由的一些授权,但对于某些路由,它不起作用,并且它要么始终授权,要么根本没有授权。

I want only users with a role id of 5 (admin) to access the update action of the prices controller, this is my ability.rb code:我只希望角色 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

The first action for index etc. is working correctly and for the second action, I debugged that the role id is found correctly aswell.索引等的第一个操作正常工作,对于第二个操作,我调试了角色 id 也正确找到。 So the fault has to be in my controller, here is my code:所以故障必须在我的控制器中,这是我的代码:

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

If I use current_user to check in the authorized method everyone can change the values, if I use an instance variable of @prices then nobody can execute the controller action.如果我使用current_user来检查授权方法,每个人都可以更改值,如果我使用@prices的实例变量,则没有人可以执行控制器操作。

I'm also handling the exception:我也在处理异常:

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

I read the documentation over and over again but I can't figure out where my fault is.我一遍又一遍地阅读文档,但我无法弄清楚我的错在哪里。

A few different comments:一些不同的评论:

In your ability.rb I'd say use以你的能力.rb 我会说使用

if user.role.name == 'admin'

instead of代替

if user.role.id == 5

as unless you set your ids manually you may well have to change this for production.除非您手动设置您的 id,否则您很可能必须更改它以进行生产。

Also

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

should be应该

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

and in your controller replace并在您的控制器中替换

authorize! :update, current_user

with

authorize! :update, Price

Usually in a rails update action you would be updating just one object and you would authorise it using:通常在 rails 更新操作中,您将只更新一个对象,并使用以下方法对其进行授权:

authorize! :update, @price

but in your case I would guess authorising via the model is your best route.但在你的情况下,我猜想通过模型授权是你最好的途径。

The ability definition should be:能力定义应该是:

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

You authorize models - not controllers.您授权模型 - 而不是控制器。

When calling authorize you should pass the resource instance you are updating or the class if there is no instance:调用授权时,您应该传递正在更新的资源实例或类(如果没有实例):

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

But the controller is fundamentally broken in itself in a way that has nothing to do with CanCanCan.但是控制器本身从根本上就以与 CanCanCan 无关的方式被破坏了。 This is where it all falls apart:这就是一切崩溃的地方:

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

where returns a collection of records - #update is a method which is called on a single record . where返回记录的集合 - #update是一种在单个记录上调用的方法。 I have no idea if this is a very naive and failed attempt at mass updates or if you're trying to do something like a slug column (pretty urls).我不知道这是否是一次非常幼稚且失败的大规模更新尝试,或者您是否正在尝试执行诸如 slug 列(漂亮的 url)之类的操作。 But you should probably stick with the rails conventions until you know what you are doing:但是您可能应该坚持使用 rails 约定,直到您知道自己在做什么:

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

In this case you can also just use load_and_authorize_resource instead of manually finding and authorizing.在这种情况下,您也可以只使用load_and_authorize_resource而不是手动查找和授权。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM