20

我刚刚从 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)
4

2 回答 2

29

从安全的角度来看,我可以看到一些值得一提的事情。例如,如果您允许用户指定 thestudent_idinstructor_idparam 字段,那么如何阻止他们为自己以外的人传递 ID?您永远不想让用户指定他们是谁,尤其是当您基于用户类型制定策略时。

对于初学者,我将实现设计并添加一个额外的布尔字段,称为instructortrue用户是教师但默认false为学生时。

然后你User的 s 会自动instructor?定义一个方法,true如果instructor列中的值为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 技能的反馈。

于 2014-08-20T00:09:59.237 回答
0

遵循@Saul 关于添加devise或其他身份验证方式的建议。

然后你会想要这样做(EntityGoal你的情况下):

@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])在控制器中。

于 2021-02-17T11:28:11.463 回答