为什么在Pundit中以“范围”为导向的行为(特别是“索引”行动)处理方式不同?

时间:2014-01-16 20:52:24

标签: ruby-on-rails authorization pundit

我正在写https://github.com/elabs/pundit#scopes

我认为授权应该回答问题您是否允许访问此资源?,即true / false回答。除index之外的所有操作都是这种情况,根据Pundit的文档,这些操作应该根据谁的要求返回不同的ActiveRecord::Relation。例如, admin 获得scope.all,而常规用户获得scope.where(:published => true)

应用/策略/ post_policy.rb

class Scope < Struct.new(:user, :scope)
  def resolve
    if user.admin?
      scope.all
    else
      scope.where(:published => true)
    end
  end
end

应用/控制器/ posts_controller.rb

def index
  @posts = policy_scope(Post)
end

我的保留意见是这是一个很滑的斜坡,很快我就会在范围内添加演示文稿(例如scope.all.order('created_at ASC')) - 而且在授权政策中感觉很奇怪>

当然我可以把它移到控制器......

def index
    @post = policy_scope(post)
    if user.admin?
        @post = @post.order( 'created_at ASC' )
    end
end

......但这是控制器的工作吗?而且我认为将这样的调用添加到视图中是不对的。那么也许它应该是一种模型方法?

你认为做以下事情的利弊是什么?

应用/控制器/ posts_controller.rb

这样就可以像其他方法一样保留index,只需调用authorize,然后调用一个模型方法。

def index
  authorize(Post)
  @posts = Post.index(current_user)
end

应用/策略/ post_policy.rb

这只是给出了一个真/假答案。你被授权了吗?是或否。

def index?
    user.admin? || user.regular_user?
end

应用/模型/ post.rb

在模型中我们可以像我们喜欢的那样花哨。

def self.index(user)
  if user.admin?
    Post.all.order('created_at ASC')
  else
    Post.where(user_id: user.id)
  end
end

思想?

1 个答案:

答案 0 :(得分:11)

我对Pundit授权与范围的理解如下:

authorization:'此用户是否允许对此资源进行操作(创建/更新/销毁)

within scope:'此用户是否应该能够看到(索引/显示)此资源?'

授权(authorize @resource)推迟到permitted_attributes ResourcePolicy获取答案。

范围(policy_scope(Resource))推迟到resolve

我认为Pundit范围背后的原因是代码中只有一个位置可以定义谁应该访问哪些资源。

正如您所描述的,您可以在控制器或视图中实现相同的行为。但是,如果您在某个控制器方法中忘记了适当的范围,那么将代码放入策略中以防止未经授权的访问。

我认为policy_scope()是限制可见性的方法,而其他结果改进(例如排序)可以在控制器级别进行。然而,毫无疑问,在游戏中有很多个人偏好。