Пользователь с членством может читать все данные

Настройка

Я использую Rails 5.2 с камнем cancancan.

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 'can'. Код проверки не может быть определен для:read #

Я думаю, что я решаю проблему с совершенно неправильной точки зрения. Как мне настроить ability.rb Для решения этой проблемы?

2 ответа

Решение

Из документации:

Почти все, что вы можете передать в хэш условий в Active Record, будет работать здесь. Единственное исключение - работа с идентификаторами моделей. Вы не можете передать объекты модели напрямую, вы должны передать идентификаторы.

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')):

organizations_with_user_role = Organization.joins(memberships: :role).
  where(memberships: {user_id: user.id}).where(roles: {name: 'User'})

Я бы использовал здесь простой хэш условий:

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(способность, чтение) и извлекать все организации, которые может прочитать пользователь.

Другие вопросы по тегам