多租户应用程序中的授权

时间:2014-03-10 21:08:34

标签: ruby-on-rails authorization cancan multi-tenant

在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

用户是否能够管理所有租户的主题或仅在租户范围内?

或者是一个更一般的问题:多租户申请的授权方法是什么?

3 个答案:

答案 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