我使用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错误。
给定权威人士的策略范围:
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作为默认情况进行过滤。我不确定在这种情况下如何使用专家作用域,但我的感觉是它需要看起来像这样:
def resolve
current_client.posts
end但current_client自然不会出现在权威人士的范围内。
一种解决方案是将current_client.posts传递给policy_scope
def index
@posts = policy_scope(current_client.posts)
end但我觉得这分散了我的租约范围,破坏了使用专家完成这项任务的目的。
有什么想法吗?或者,我是不是超出了它的设计目标?
发布于 2015-08-06 09:45:47
处理这个问题最“权威”的方法是在你的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使用where:source实现方法show。
因此,如果您的作用域不返回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:
class PostsController < ApplicationController
def index
@posts = policy_scope(Post)
end
def show
@post = Post.find(params[:id])
authorize @post
end
endhttps://stackoverflow.com/questions/30990700
复制相似问题