如何使用权威范围?

时间:2014-01-31 00:47:59

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

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

2 个答案:

答案 0 :(得分:18)

从安全角度来看,我可以看到一些值得一提的东西。例如,如果您允许用户指定student_idinstructor_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 或其他身份验证方式。

然后你会想要这样做(在你的情况下,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]) 在控制器中。