我刚从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)
OR
@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访问它的属性,但是没有用。
class Scope < Struct.new(:user, :scope)
答案 0 :(得分:18)
从安全角度来看,我可以看到一些值得一提的东西。例如,如果您允许用户指定student_id
和instructor_id
param字段,那么阻止他们为自己以外的人传递ID的是什么?您不希望让用户指定他们是谁,尤其是当您根据用户类型制定策略时。
对于初学者,我会实现Devise并添加一个名为instructor
的额外布尔字段,当用户是教师时会true
,但学生默认为false
。
然后,您的User
会自动定义instructor?
方法,如果true
列中的值为instructor
,则会返回true
。< / p>
然后,您可以为学生添加帮助:
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技能的反馈。
答案 1 :(得分:0)
按照@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])
在控制器中。