【问题标题】:How to use pundit scopes?如何使用权威范围?
【发布时间】:2014-01-31 00:47:59
【问题描述】:

我刚刚从 CanCan 切换到 Pundit。我不确定有几件事,以及如何最好地使用 Pundit。

例如:

如果您的资源可以有多个父对象,例如,假设一个目标属于学生和教师。因此,一个学生可以有很多目标,而一个教师可以有很多目标。在控制器索引操作中,您可能会这样做:

if params[:student_id].present?
  @account = Student.find(params[:student_id])
  @goals = @account.goals
elsif params[:instructor_id].present?
  @account Instructor.find(params[:instructor_id])
  @goals = @account.goals
end

params 在策略内部是不可用的,所以逻辑需要在这里完成。我认为。据我所知,如果您跳过policy_scope,您将在查看目标索引页面时收到未经授权的错误。

你会:

@goals = policy_scope(@account.goals)

@goals = policy_scope(Goal.scoped).where( account_id: @account.id)

当您在混合中添加一堆包含时会发生什么?

  @example = policy_scoped(@school.courses.includes(:account => :user, :teacher ))

或者当需要订购时...这样正确吗?

 policy_scope(Issue.scoped).order("created_at desc")

使用作用域时:这里的:scope 是什么? :scope 是被评估模型的实例吗?我尝试通过:scope 访问它的属性,但没有成功。

  class Scope < Struct.new(:user, :scope)

【问题讨论】:

    标签: ruby-on-rails-4 authorization pundit


    【解决方案1】:

    从安全的角度阅读本文,我可以看到一些值得一提的事情。例如,如果您允许用户指定 student_idinstructor_id 参数字段,有什么办法可以阻止他们为自己以外的人传递 ID?您永远不想让用户指定他们是谁,尤其是当您根据用户类型制定政策时。

    对于初学者,我将实现 Devise 并添加一个名为 instructor 的额外布尔字段,当用户是教师时为 true,但对于学生,默认为 false

    那么您的Users 将自动定义一个instructor? 方法,如果instructor 列中的值为true,该方法将返回true

    然后您可以为学生添加一个助手:

    def student?
      !instructor?
    end
    

    现在使用 Devise(它使我们能够访问 current_user 变量)我们可以执行 current_user.instructor? 之类的操作,如果他们是讲师,它将返回 true

    现在谈谈政策本身。我几周前才开始使用 Pundit,但在您的情况下,我会这样做:

    class GoalPolicy < ApplicationPolicy
      class Scope < GoalPolicy
        attr_reader :user, :scope
    
        def initialize(user, scope)
          @user  = user
          @scope = scope
        end
    
        def resolve
          @scope.where(user: @user)
        end
      end
    end
    

    那么您的(我假设 GoalsController 类和 index 方法)方法可以如下所示:

    def index
      policy_scope(Goal) # To answer your question, Goal is the scope
    end
    

    如果你想订购,你也可以做

    def index
      policy_scope(Goal).order(:created_at)
    end
    

    我才发现你半年前问过这个问题,但是,嘿!也许它会回答其他人的一些问题,也许我会得到一些关于我自己崭露头角的 Pundit 技能的反馈。

    【讨论】:

    • 使用getter方法在resolve中获取userscope可能会更好,也让它们成为私有接口的一部分。
    【解决方案2】:

    按照@Saul 的建议添加devise 或其他身份验证方式。

    然后你会想要这样做(Entity 在你的情况下是 Goal):

    @entities = policy_scope(Entity).where(...)
    

    entity_policy.rb:

    class EntityPolicy < ApplicationPolicy
      class Scope < ApplicationPolicy::Scope
        def resolve
          # Here you have access to `scope == Entity` and `user == current_user`
          scope.where(entity: user.entity)
        end
      end
    end
    

    您可能想知道为什么 where 会重复。答案是(这是您问题的答案):它们有不同的用途。尽管目前它们是相同的,但请考虑一下:

    您现在拥有一个可以访问所有内容的admin 用户。您的政策变更:

    class EntityPolicy < ApplicationPolicy
      class Scope < ApplicationPolicy::Scope
        def resolve
          if user.admin?
            scope.all
          else
            scope.where(entity: user.entity)
          end
        end
      end
    end
    

    如果您的组织有目标和以下宁静端点:

    /organizations/:organization_id/goals

    当用户访问/organizations/1/goals 时,您要确保该用户仅在该用户是组织成员时才被允许访问目标:

    scope.where(organization: user.organization) 在政策中

    您还想确保当管理员访问时,他们只能看到与该组织相关的目标:

    policy_scope(Goal).where(organization_id: params[:organization_id]) 在控制器中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多