【问题标题】:Pundit scoping usage empty resultsPundit 范围使用空结果
【发布时间】:2016-06-11 12:36:28
【问题描述】:

假设我有一个场景,我们有Users,每个用户都可以创建自己的Projects

我正在尝试将我的 Rails 控制器的 Show 操作限制为仅允许管理员或项目所有者通过 Show 操作。

我面临的问题是,也许我对如何在 Pundit 中使用 Scopes 有误解。

我的Show 操作如下所示:

  def show
    project = policy_scope(Project).find_by({id: project_params[:id]})

    if project
      render json: project
    else
      render json: { error: "Not found" }, status: :not_found
    end
  end

我的 Pundit Scope 类如下所示:

  class Scope < Scope

    def resolve
      if @user.admin?
        scope.all
      else
        # obviously, if non-matching user id, an ActiveRelation of  
        # empty array would be returned and subsequent find_by(...) 
        # would fail causing my controller's 'else' to execute
        # returning 404 instead of 403
        scope.where(user_id: @user.id)
      end
    end
  end

在我的 Rails 测试中,我试图断言非项目所有者应该收到 403 禁止:

test "show project should return forbidden if non admin viewing other user's project" do
  # "rex" here is not the owner of the project
  get project_path(@project.id), headers: @rex_authorization_header
  assert_response :forbidden
end

我的测试失败了。我收到错误消息:

Failure:
ProjectsControllerTest#test_show_project_should_return_forbidden_if_non_admin_viewing_other_user's_project [/Users/zhang/App_Projects/LanceKit/Rails_Project/LanceKit/test/controllers/projects_controller_test.rb:40]:
Expected response to be a <403: forbidden>, but was a <404: Not Found>.
Expected: 403
  Actual: 404

我不太觉得我在正确使用 Pundit。

我应该使用 Pundit 的 authorize project 而不是使用 policy_scope(Project)... 来执行 Show 操作吗?

我期待 scope.where(...) 检测到错误的用户 ID 并返回一些错误,说“您无权查看此资源”而不是返回结果。

【问题讨论】:

标签: ruby-on-rails pundit


【解决方案1】:

根据我的测试结果显示,show 操作的使用范围错误

我的发现告诉我 Pundit 范围仅用于过滤一组数据以仅返回与条件匹配的数据,它不检查 current_user 是否是资源的所有者。 Pundit 范围不会引发 403 Forbidden 错误。

换句话说,仅在 show 操作中使用范围会导致语义错误,例如 this project with id 3 does not exist in the database 而不是 you are not authorized to view this project because it belongs to a different user

自我总结:

  • policy_scope 用于index 操作
  • authorize 用于showcreateupdatedelete
  • 如果您不是资源所有者并尝试访问一些时髦的复数资源路径,请使用 authorizepolicy_scope,例如

    get "/user/1/projects" =&gt; "Project.index"

    如果您想检查用户是允许查看您的项目的“项目经理”还是“合作者”。在这种情况下,您可能需要使用额外的 elsif 子句来修改您的范围代码。

关于我的上述问题,我修改了我的项目以在我的show 操作中使用authorize

def show
    project = Project.find_by({id: project_params[:id]})

    authorize project

    if project
      render json: project
    else
      render json: { error: "Not found" }, status: :not_found
    end
  end

这会引发预期的 403 Forbidden 错误,这是我的测试所期望的,因此我的测试通过了。

【讨论】:

  • 我同意这一点,当您访问节目时,您要求的是特定记录。 Pundit 范围仅对索引(或其他收集方法)有意义。
【解决方案2】:

Pundits docs regarding scopes 声明您确实可以将它们用于显示操作:

def index
  @posts = policy_scope(Post)
end

def show
  @post = policy_scope(Post).find(params[:id])
end

如果用户(手动)打开一个带有实例 id 参数的 URL,那么仅使用 authorize 可能还不够,她不应该查看。

为避免出现RecordNotFound 错误,我使用了recommended NilClassPolicy

class NilClassPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      raise Pundit::NotDefinedError, "Cannot scope NilClass"
    end
  end

  def show?
    false # Nobody can see nothing
  end
end

【讨论】:

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