我使用Pundit进行授权,我想利用其多租户范围机制(由主机名驱动)。
我通过以下方式手动执行此操作:
class ApplicationController < ActionController::Base
# Returns a single Client record
def current_client
@current_client ||= Client.by_host(request.host)
end
end
然后在我的控制器中执行以下操作:
class PostsController < ApplicationController
def index
@posts = current_client.posts
end
end
非常标准的票价,真的。
我喜欢Pundit的verify_policy_scoped
过滤器的简单性,以确保绝对每个操作都限定为正确的Client
。对我来说,如果未正式执行范围确定,那么确实值得500错误。
鉴于Pundit政策范围:
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
# have access to #scope => Post class
# have access to #user => User object or nil
end
end
end
现在,Pundit似乎希望我按用户过滤Post
,例如:
def resolve
scope.where(user_id: user.id)
end
但是,在这种情况下,我实际上希望按current_client.posts
过滤作为默认情况。我不确定如何在这种情况下使用Pundit示波器,但我的感觉是它需要看起来像:
def resolve
current_client.posts
end
但是current_client
自然不会在Pundit范围内可用。
一种解决方案可能是将current_client.posts
传递给policy_scope
:
def index
@posts = policy_scope(current_client.posts)
end
但是我觉得这可以分散我的租约范围,从而破坏了使用Pundit完成这项任务的目的。
有什么想法吗?还是我驾驶Pundit超出它的设计目标?
答案 0 :(得分:1)
处理此问题的最“权威兼容”方法是在Post
模型中创建范围:
Class Post < ActiveRecord::Base
scope :from_user, -> (user) do
user.posts
end
end
然后,您就可以在自己的政策中使用它,其中user
填充了您的控制器中的current_user
:
class PostPolicy < ApplicationPolicy
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.from_user(user)
end
end
end
如果从作用域返回ActiveRecord :: Relation,则可以从此处停止读取。
默认ApplicationPolicy
使用show
实施方法where
:
source
因此,如果您的作用域不返回AR :: Relation而是返回数组,那么可以使用一种解决方法来覆盖此show
方法:
class PostPolicy < ApplicationPolicy
class Scope
# same content than above
end
def show?
post = scope.find do |post_in_scope|
post_in_scope.id == post.id
end
post.present?
end
end
无论您的实施是什么,您只需要使用控制器中的PostPolicy
“Pundit-way”:
class PostsController < ApplicationController
def index
@posts = policy_scope(Post)
end
def show
@post = Post.find(params[:id])
authorize @post
end
end