在Railscasts Episode 388 - Multitenancy with Scopes中,Ryan正在添加默认范围以确保安全性:
或者我们可以使用CanCan等授权库来处理范围,但这不适用于多租户应用程序,并且不能很好地解决这个问题。这是一种可以使用默认范围的情况,这就是我们要做的事情。
class Tenant < ActiveRecord::Base
attr_accessible :name, :subdomain
has_many :topics
end
class Topic < ActiveRecord::Base
attr_accessible :name, :content
belongs_to :user
has_many :posts
default_scope { where(tenant_id: Tenant.current_id) }
end
我的问题是:我想实施授权(例如使用Cancan)并希望定义这样的功能:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, Topic
else
can :read, Topic
end
end
end
用户是否能够管理所有租户的主题或仅在租户范围内?
或者是一个更一般的问题:多租户申请的授权方法是什么?
答案 0 :(得分:4)
您使用CanCan或CanCanCan是正确的轨道,因为我认为CanCan已被弃用。
我不喜欢default_scope
,因为它不是threadsafe。用户标识存储在类变量中,这意味着应用程序中的两个或多个并发用户将断开此操作,除非您使用Unicorn或其他一些Web服务器确保只有一个客户端连接将访问同一个帖子。
因此你应该使用像Cancan这样的东西。
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
# User's own Topics only:
can :manage, Topic, user_id: user.id
# or, with a Tenant
can :manage, Topic, tenant_id: user.tenant.id if user.tenant # User belongs_to Tenant
can :manage, Topic, tenant_id: user.tenants.map(&:id) if user.tenants.any? # User has_many Tenants
else
can :read, Topic # Anyone can read any topic.
end
end
end
从上面的三个例子中选择你需要的策略。
编辑多租户管理员在评论中对@JoshDoody的问题稍微复杂一点的例子:
class Admin < User; end
class TenantAdmin
belongs_to :tenant
belongs_to :admin, class_name: User
end
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, Topic, tenant_id: TenantAdmin.where(admin: user).map(&:tenant_id)
else
can :read, Topic # Anyone can read any topic
end
end
end
现在,它可能不是您想要的高效,但总的想法是您有TenantAdmins,他们将能够在他们的租户中管理主题。
希望这有帮助。
答案 1 :(得分:2)
您已为主题设置了能力。因此,它只会检查主题对象。
要检查租户级别,您需要设置如下内容:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, Tenant do |tenant|
if user.admin?
`you code goes here`
else
end
end
can :read, Tenant
can :read, Topic
end
end
答案 2 :(得分:1)
以多功能应用程序的能力为例
class Ability
def initialize(admin, tenant = nil)
user ||= User.new
if user.admin?
can :manage, Topic
else
can :manage, PostState, tenant: tenant
end
end
end
您可能无法通过租户并使用Tenant.current_id