假设我们在ruby-on-rails(API)应用程序中进行了以下设置:
class User < ActiveRecord::Base
has_many :posts
has_many :friends, class_name: User # Via a joins table....
end
class Post
belongs_to :user
end
访问/users/:id/posts
时,我希望登录用户只能查看此数据(如果他们是朋友)。
Pundit的标准实现是使用policy scope:
class PostsController < ApplicationController
before_action :authenticate
def index
posts = policy_scope(Post.where(user_id: params[:user_id]))
render posts
end
end
class PostsPolicy < ApplicationPolicy
class Scope < ApplicationPolicy::Scope
def resolve
scope.where(user: user.friends)
end
end
end
此将阻止用户看到非朋友&#39;帖子。但是,会产生200 Success
的API响应(空响应体),而不是403 Forbidden
- 这对于FrontEnd来说更适合接收,并显示相应的错误消息。
这是一个不工作的解决方案:
class PostsController < ApplicationController
before_action :authenticate
def index
posts = policy_scope(Post.where(user_id: params[:user_id]))
authorize posts # <- !!!!
render posts
end
end
class PostsPolicy
def index?
record.all? { |post| user.friends_with?(post.user) }
end
end
这不仅非常低效,而且如果用户没有任何帖子,那么您将始终获得200 Success
响应 - 这仍然不是理想的。
同样,如果响应为空,则返回403
并不理想&#34; - 因为那时你在收到朋友时会收到错误信息。帖子,如果他们没有!
这是一个可能的解决方案,但感觉错误 ...
class PostsController < ApplicationController
before_action :authenticate
def index
user = user.find(params[:user_id])
authorize(user, :index_posts?) # <-- !!!!
posts = policy_scope(Post.where(user: user))
render posts
end
end
class UsersPolicy
def index_posts?
user.friends_with?(record)
end
end
(您也可以使用更通用的方法名称,如UserPolicy#friends?
,以达到相同的效果。)
这很有效,但在授权UserPolicy
资源时,感觉就像是Pundit错误地使用了Post
方法!
Pundit不允许passing additional arguments to policies。多年来,这一直是highly requested功能。特别是,请参阅this highly-relevant PR/discussion。换句话说,我喜欢能够做到的是:
class PostsController < ApplicationController
before_action :authenticate
def index
user = User.find(params[:user_id])
posts = policy_scope(Post.where(user: user))
authorize(posts, user) # <- !!!! (not valid in Pundit)
render posts
end
end
class PostsPolicy
def index?(other_user) # <- !!!! (not valid in Pundit)
user.friends_with?(other_user)
end
end
但是,该功能最终被项目维护者最终拒绝使用&#34;名称间隔的策略&#34;和&#34;形成对象&#34;。
希望这个问题不是太过于opinion opinion opinion&&&#34;,但你会建议什么?在使用适当的200
vs 403
做出适当回应时,是否有一种干净的方式来使用Pundit库?
或者,是否有一个很好的补丁/分支/替代方案我可以使用(最好是很容易迁移到大型项目),这样可以更好地支持我想要的行为?