具有成员身份的用户可以读取所有数据

时间:2018-08-01 14:47:54

标签: ruby-on-rails authorization cancan cancancan

设置

我将cancancan gem与Rails 5.2一起使用。

rails g scaffold User first_name email:uniq
rails g scaffold Organization name:uniq
rails g scaffold Role name
rails g scaffold Membership user:references organization:references role:refences

user.rb

class User < ApplicationRecord
  has_many :memberships
  has_many :roles, through: :memberships
  has_many :organizations, through: :memberships
end

membership.rb

class Membership < ApplicationRecord
  belongs_to :role
  belongs_to :organization
  belongs_to :user
end

organization.rb

class Organization < ApplicationRecord
  has_many :memberships
  has_many :users, through: :memberships
end

role.rb

class Role < ApplicationRecord
  has_many :memberships
  has_many :users, through: :memberships
end

seeds.rb

admin = Role.create(name: 'Admin')
user = Role.create(name: 'User')

abc = Organization.create(name: 'Abc Inc.')

bob = User.create(first_name: 'Bob')
alice = User.create(first_name: 'Alice')

Membership.create(role: user, company: abc, role: user)
Membership.create(role: admin, company: abc, role: admin)

任务

管理员应该能够管理其所管理的公司的所有用户和成员身份。用户只能读取该公司的所有用户和会员资格。

这是我的cancancan配置:

ability.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new

    user_role = Role.find_by_name('User')
    admin_role = Role.find_by_name('Admin')

    organizations_with_user_role = Organization.includes(:memberships).
      where(memberships: {user_id: user.id, role_id: user_role.id})
    organizations_with_admin_role = Organization.includes(:memberships).
      where(memberships: {user_id: user.id, role_id: admin_role.id})

    can :read, Organization, organizations_with_user_role
    can :manage, Organization, organizations_with_admin_role
  end
end

然后我尝试在视图中运行此代码:

<% if can? :read, organization %><%= link_to 'Show', organization %><% end %>

这将导致错误页面显示:

可以吗?不能吗?调用不能与原始sql“可以”定义一起使用。无法为:read#

确定检查代码

我想我从一个完全错误的角度解决了这个问题。我该如何设置ability.rb来解决此问题?

2 个答案:

答案 0 :(得分:2)

我将在此处使用简单的条件散列:

can :read, Organization, memberships: { user_id: user.id, role: { name: 'User' } }
can :manage, Organization, memberships: { user_id: user.id, role: { name: 'Admin' } }

这应该足够了,您的好处是,使用这种语法,您还可以调用Organization.accessible_by(ability,:read)并检索用户可以读取的所有组织。

答案 1 :(得分:1)

来自documentation

  

几乎所有您可以传递给Active条件散列的内容   记录将在这里工作。唯一的例外是使用模型ID。   您不能直接传递模型对象,必须传递   ID。

[09:06:56] Error - typescript - src\...\TruckDeliverySchedule.tsx(154,27): error TS2461: Type 'IterableIterator<any>' is not an array type.

因此,请尝试以下操作:

can :manage, Project, group: { id: user.group_ids }

另外,为什么要使用can :read, Organization, id: organizations_with_user_role.pluck(:id) 而不是includes?您的查询可以简化为(不需要joins):

user_role = Role.find_by_name('User')