CanCan Rails 如何限制用户读取非他们创建的数据

CanCan Rails How to Limit Users From Reading Data They Didn't Create

我在 Rails 3.2 应用程序中使用 CanCan 1.6.1。我在我的控制器中使用 authorize_resource 来限制用户可以 :read, :create, :update: and :delete 的内容。

在我的 V2Posts controller 中,authorize_resource 调用对我的 :show 操作无效。

在我的能力文件中,我试图限制用户只能阅读他们自己的帖子。没有错误,但不幸的是,他们可以查看其他用户的帖子。

class V2PostsController < ApplicationController
  layout 'panel_layout', only: :show
  before_filter :redirect_if_existing, only: [:new, :create]

  authorize_resource

  def show
    @v2_post = V2Post.find(params[:id], include: :user_updates)
    @organization = @v2_post.organization

    respond_to do |format|
      format.html
      format.js
    end
  end
  ...
end

ability.rb:

class Ability
  include CanCan::Ability

  @user = nil

  def initialize(user)
    alias_action :taco,
                 :sandwich,
                 to: :read

    @user = user

    if user.is_in_role?(:admin)
      can :manage, :all
    else
      default_rules

      user.roles_list.each do |role|
        meth = :"#{role}_rules"
        send(meth) if respond_to? meth
      end
    end
  end

  def default_rules
    cannot [:read, :create, :update, :destroy], :all
    ...
  end

  def pro_user_rules
    can :read, V2Post, proid: @user.id
  end
...
end

models/v2_post.rb:

class V2Post < ActiveRecord::Base
  attr_accessible :proid, :user_updates_attributes, :organization

  belongs_to :user,  :foreign_key => "proid"
  has_many :user_updates, as: :pro_post, dependent: :destroy
  accepts_nested_attributes_for :user_updates

end

load_and_authorize_resource 用于阻止用户查看其他用户的帖子。但是,它会为我加载资源,还会为其他操作添加额外的数据库调用。 authorize_resource 不应该适用于我的情况吗?我已经在 show 动作中明确定义了 @v2_posts (我自己加载资源)。为什么它没有传递给 authorize_resource 调用?

鉴于此,load_and_authorize_resourceauthorize_resource 哪个效果更好,每个 benefits/drawbacks 是多少?我已经阅读了文档,我很困惑。

问题是 authorize_resource 被 运行 用作前过滤器,并且直到在您的操作中 - 在调用 authorize_resource 之后才设置实例。当实例为nil时,authorize_resource方法默认授权classV2Report。授权 class 忽略属性条件(例如 can :read V2Report, user_id: @v2_post.user_id)。

您可以使用 load_and_authorize 资源,并在每次操作前让 CanCan 在您的控制器中设置实例变量。对于以更复杂的方式加载资源的操作,例如您的 V2Post.find(params[:id], include: :user_updates),您可以创建自定义 prepend_before_filter 方法来为特定操作加载资源。只要它是前置的,加载的实例就会被传递给 authorize_resource 方法。如果实例已经加载,load_and_authorize_resource 将不会对数据库进行额外的调用。

class V2PostsController < ApplicationController
  prepend_before_filter :custom_load_resource, only: [:show, :edit]
  load_and_authorize_resource

  def show
    render @v2_post
  end

  private

  def custom_load_resource
    @v2_post = V2Post.find(params[:id], include: :user_updates)
  end

end

您也可以完全忘记 load_resource,并在 100% 的时间使用您自己的 before_filter。或者,您可以放弃过滤器并从操作中调用 authorize!。根据我最近的经验,上面详述的方法非常适用于 RESTful 控制器。