Pundit 范围界定使用空结果
Pundit scoping usage empty results
假设我有一个场景,我们有 Users
,每个用户都可以创建自己的 Projects
。
我试图将我的 Rails 控制器的 Show
操作限制为仅允许管理员或项目所有者能够执行 Show
操作。
我面临的问题是,也许我对如何在 Pundit 中使用范围有误解。
我的 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
我的专家范围 class 看起来像这样:
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 和 return 一些错误说 'you are not authorized to view this resource' 而不是 return 结果。
从我的测试结果来看,show
操作的范围是 错误的。
我的发现告诉我 Pundit 范围仅用于将一组数据过滤为仅 return 符合条件的数据,它不会检查 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
操作
- 对
show
、create
、update
、delete
使用 authorize
使用 authorize
AND policy_scope
如果您不是资源所有者并尝试访问一些时髦的复数资源路由,例如
get "/user/1/projects" => "Project.index"
如果您想检查用户是否被允许查看您的项目的 "project manager" 或 "collaborator"。在这种情况下,您可能需要使用额外的 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 禁止错误,因此我的测试通过了。
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
假设我有一个场景,我们有 Users
,每个用户都可以创建自己的 Projects
。
我试图将我的 Rails 控制器的 Show
操作限制为仅允许管理员或项目所有者能够执行 Show
操作。
我面临的问题是,也许我对如何在 Pundit 中使用范围有误解。
我的 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
我的专家范围 class 看起来像这样:
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 和 return 一些错误说 'you are not authorized to view this resource' 而不是 return 结果。
从我的测试结果来看,show
操作的范围是 错误的。
我的发现告诉我 Pundit 范围仅用于将一组数据过滤为仅 return 符合条件的数据,它不会检查 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
操作 - 对
show
、create
、update
、delete
使用 使用
authorize
ANDpolicy_scope
如果您不是资源所有者并尝试访问一些时髦的复数资源路由,例如get "/user/1/projects" => "Project.index"
如果您想检查用户是否被允许查看您的项目的 "project manager" 或 "collaborator"。在这种情况下,您可能需要使用额外的
elsif
子句修改范围代码。
authorize
关于我的上述问题,我修改了我的项目以在我的 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 禁止错误,因此我的测试通过了。
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