Rails Pundit 的公司特定用户权限

Rails Company-specific user permissions with Pundit

我实际上不确定这是 Pundit 还是一般权限架构问题,但我设置了一个简单的 Pundit 策略来限制公司内成员可以执行的操作。用户以 has_many, through: 关系作为会员加入公司。 Member 模型的 role 属性为 owneruser.

给定一个用户是商店的成员,我如何限制控制器对用户与商店的关联的访问?下面是一个Admin::MembersController,店主可以在其中邀请其他成员。我如何通过他们与商店的会员协会将此限制为专家中的给定用户?下面的策略不起作用,返回一个记录数组。如果我只检查第一条记录它有效但我觉得那是因为我的理解有限。

All of the tutorials and documentation I see online for CCC and Pundit involve application-wide permissions. But I need more granular control.

For example, my application has hundreds of companies. Each company has a user who is an "owner" and they login each day to look at their earnings information. That owner/user wants to invite Joe Smith to the application so they can also look at the data and make changes. But they don't want Joe Smith to be able to see certain types of data. So we restrict Joe Smith's access to certain data for that company.

class Admin::MembersController < Admin::BaseController

  def index
    @company_members = current_company.members
    authorize([:admin, @company_members])
  end
end

政策

class Admin::MemberPolicy < ApplicationPolicy

  def index?
    return [ record.user_id, record.store_id ].include? user.id
    ## this works return [ record.first.user_id, record.first.store_id ].include? user.id
  end
end

User.rb

class User < ApplicationRecord
  # Automatically remove the associated `members` join records
  has_many :members, dependent: :destroy
  has_many :stores, through: :members
end

Member.rb

class Member < ApplicationRecord
  belongs_to :store
  belongs_to :user

  enum role: [ :owner, :user ]
end

Store.rb

class Store < ApplicationRecord
  has_many :members
  has_many :users, through: :members
end

我认为您正在寻找的是 pundit 中的范围。您希望将某些数据访问限制为商店成员,并将该数据显示给该商店的所有者。

为此,您需要根据用户角色更改查询

像这样:

class EarningPolicy < ApplicationPolicy
  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user  = user
      @scope = scope
    end

    def resolve
      # here check for owner and show all data
      if user.members.owner # here you query your memberships to find the owner role.
        scope.all
      else
        # here show only data relevant to member
        scope.where(published: true)
      end
    end
  end
end

您现在可以在控制器中像这样使用 class

def index
  @earnings = earning_scope(Earning)
end

希望对您有所帮助

我从 Pundit 的贡献者那里得到了一些见解;最合理的方法是使用域对象来表示用户所在的上下文——在自述文件 (https://github.com/varvet/pundit#additional-context) 中有关于此的信息。 UserContext 对象将提供对用户和组织的引用。

class ApplicationController
  include Pundit

  def pundit_user
    if session[:organization_id]
      UserContext.new(current_user, Organization.find(session[:organization_id]))
    else
      UserContext.new(current_user)
    end
  end
end

class UserContext
  attr_reader :user, :organization

  def initialize(user, organization = nil)
    @user           = user
    @organization   = organization
  end
end