cancancan如何设置模型实例,以及我如何检查它



我注意到用户可以访问他们不应该访问的操作。

我在rails控制台上用之类的东西调试

user = User.first 
physician = Physician.first 
ability = Ability.new(user)
ability.can?(:send_message, physician)
# => false

上面说,用户不能访问该医生的send_message操作,这是所需的行为,但我知道他们可以在应用程序中访问!

认为这将原因缩小到由于某种原因无法加载错误的模型实例。这在cancan文档中也有暗示:

注意:这假设模型实例加载正确。

但问题是我不确定如何从这里诊断问题,因为控制台说它应该可以工作。我不知道如何查看cancancan设置的模型实例,也不知道还能尝试什么。

有什么想法吗?

更新

我确实通过在控制器中使用authorize! :send_message, physician解决了这个问题,由于我只是偶然发现了这种行为,我认为更重要的是弄清楚为什么加载了错误的模型实例(尤其是这样我就可以看到其他地方是否也发生了这种情况)。

我弄清楚了为什么会发生这种事(但我仍然不知道如何否认)

我认为发生这种情况是因为我有很多自定义操作,有些操作有@physician = Physician.find(current_user.physician.id)(即他们是当前用户),而另一些操作更像@physician = Physician.find_by_id(physician_params[:id])。我不确定cancan是如何设置实例模型的,但我知道它不是通灵的,所以它不知道是将其设置为当前用户的医生实例,还是传入的医生id的医生实例。

剩下什么

cancancan如何为自定义方法设置模型实例(我认为它尝试了一些东西,如果不起作用,就尝试其他东西,等等)?

有帮助的小笔记:

  • load_and_authorize_resource尝试加载非RESTful操作的模型实例
  • 文档中的一些有用信息
  • 这可能与我的经历有关:

当我返回slug时,它打破了这种行为,我可以编辑所有pokemon。

把我的笔记留在这里,以防它们对其他人有帮助。

TL;DR,cancancan有很多细微的假设,你从一开始就不知道。我通过彻底阅读cancancan自述文件、代码和定义能力文档中的注释发现了其中的许多

好了。。

cancancan的工作原理

  • 如果在控制器操作本身中调用authorize!,cancancan将在每个控制器操作中查找一个实例变量
  • 如果您只是在控制器的开头添加load_and_authorize_resource,则会做两件事:
    1. 加载cancancan认为应该加载的实例变量,并且
    2. 检查该模型实例的授权
  • 请注意,对于自定义操作,load_and_authorize_resource仍然会尝试加载模型实例,但它如何知道要加载什么?它猜测不会,对我来说,我不喜欢,所以要注意这一点。
    • 对我来说,我更喜欢自己分两个步骤来完成load_and_authorize_resource的工作,所以我确切地知道发生了什么。
      1. 确保@article是通过每个控制器操作的before操作生成的(或通过@articles生成索引操作)
      2. 只需在控制器顶部写一行,在设置模型实例的before操作之后写load_and_authorize_resource
        • 请注意,现在唯一的区别是开发人员负责加载正确的模型实例,而cancancan并没有试图猜测。我更喜欢这种方法,因为它只需要一个错误就可以意外地允许访问不应该授予的地方
  • 还要记住,load_and_authorize_resource应该始终在设置模型实例变量的任何操作之前

可能也有帮助的随机笔记

  • 实例变量的名称取决于操作。如果我们有一个物品控制器,那么:

    • 对于索引操作,authorize查找@articles
    • 对于所有其他操作,授权查找@article
      • 然后它检查用户是否被允许访问该资源
  • load_and_authorize_resource检查模型实例是否已设置,如果未设置,则设置一个。因此,如果您有一个创建@article/@articles的before操作,那么load_and_authorize_resource不会为您执行该操作(即不会覆盖它),但如果您没有设置,cancan将尝试设置一个。请参阅源代码的注释:

如果实例变量已设置,则不会加载资源。这使得通过对某些操作执行before_action来覆盖行为变得容易。

  • 能力规则将覆盖以前的规则。(示例请参见此处)
  • 最后一件事,永远不要在ability.rb中使用current_user,它会无声地出错(!!),所以一定要使用user:)

以下是发生的情况:https://github.com/CanCanCommunity/cancancan/blob/585e5ea54c900c6afd536f143cde962ccdf68607/lib/cancan/controller_additions.rb#L342-L355


# Creates and returns the current user's ability and caches it. If you
# want to override how the Ability is defined then this is the place.
# Just define the method in the controller to change behavior.
#
#   def current_ability
#     # instead of Ability.new(current_user)
#     @current_ability ||= UserAbility.new(current_account)
#   end
#
# Notice it is important to cache the ability object so it is not
# recreated every time.
def current_ability
@current_ability ||= ::Ability.new(current_user)
end

最新更新