如何响应403(禁止)对嵌套资源的索引操作?

时间:2017-07-05 09:47:15

标签: ruby-on-rails ruby authentication pundit

假设我们在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库?

或者,是否有一个很好的补丁/分支/替代方案我可以使用(最好是很容易迁移到大型项目),这样可以更好地支持我想要的行为?

0 个答案:

没有答案