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