Динамический поиск Rails на основе роли
-
07-07-2019 - |
Вопрос
Я ищу лучший способ создания чистого способа создания средств поиска на основе ролей/авторизаций?
В моей схеме модели user
может иметь одну из нескольких (определяемых администратором) ролей, например Администратор, Региональный менеджер, Помощник по продажам:
Пример Имея пользователя с ролью регионального менеджера и присоединенного к региону A, я хотел бы иметь возможность запрашивать, что другие пользователи могут видеть, например:
regional_manager_for_region_a.users
=> [...] # Array of users joined to region a
regional_manager_for_region_b.users(:all, conditions => { :active => true })
=> [...] # Array of active users joined to region b
administrator.users
=> [...] # Array of all users in system
Спасибо, очень ценю любую помощь!
Решение
Я думаю, вам нужно установить какой-то механизм авторизации.
Лучшая жемчужина, которую я знаю для этого - Declarative_authorization . Я лично использовал это в производственной среде, и я доволен этим. Об этом тоже есть railscast .
Идея состоит в том, что вы объявляете в одном конкретном файле ( config / authorization_rules.rb
) " роли и разрешения " ;. Вы говорите что-то вроде «менеджер может читать только клиенты, связанные с ним» или "администратор может читать и писать всем пользователям". В вашем случае это будет выглядеть так:
authorization do
role :guest do
# actions here can be done by everyone, even not logged in people
end
role :user do
includes :guest
# actions here can be done by logged people
end
role :manager do
includes :user #managers do everything users do, plus:
has_permission_on :sales_region, :to => :read do
if_attribute :id => is_in {user.sales_region_ids}
end
has_permission_on :users, :to => [:update, :read] do
if_attribute :id => is {user.user_ids_by_sales_region} #defined on the model
end
end
role :admin do
includes :user
has_permission_on [:sales_regions, :users], :to :manage
end
end
privileges do
privilege :manage do
includes :create, :read, :update, :delete
end
end
Как только это указано, вы должны изменить ваши модели, чтобы они использовали Declarative_authorization
. Также давайте определим метод user_ids_by_sales_region
class User < ActiveRecord::Base
using_access_control # this enables DA
def users_by_sales_region
sales_regions.collect{ |sr| sr.users }.flatten.uniq
end
def user_ids_by_sales_region
users_by_sales_region.collect{ |u| u.id }
end
end
У вас также должен быть метод current_user
и способ получения ролей текущего пользователя. См. «Предоставление требований к плагину» " раздел readme .
Затем вы можете делать то, что вы хотите, с помощью with_permissions_to
:
manager = User.find(...)
manager.users.with_permissions_to(:read) # the users from his region
manager.users.with_permissions_to(:read).find(:all, conditions => { :active => true })
manager.users.with_permissions_to(:write) #returns no users, managers can't edit them
admin = User.find(...)
admin.users.with_permissions_to(:write) #will return all users
Вначале это потребует немного усилий, но значительно упростит приложение. Кроме того, у вас есть дополнительные функции, такие как скрытие / отображение частей представлений в зависимости от разрешений, которые есть у текущего пользователя, а также запрет доступа к определенным действиям контроллера.
Кроме того, он должен прекрасно работать с нумерацией страниц и т. д.
Существует еще один декларативный гем авторизации, который называется cancan . У меня нет опыта с этим, но если это делает Райан Бейтс, это должно быть хорошо (у него есть railscast для него тоже). Тем не менее, я не думаю, что это позволяет расширения модели, а это то, что вам сейчас нужно.
Другие советы
Мой ответ ниже подойдет для простых искателей;однако он не очень гибок и несовместим с will_paginate
плагин.Кто-нибудь знает лучший способ четкого определения пользователей? @current_user
способен управлять?
Спасибо
Только что ответил на свой вопрос, переопределив расширение ассоциации по умолчанию, как показано ниже.Хотя было бы здорово узнать комментарии или альтернативы!
class User < ActiveRecord::Base
has_many :users do
def find(*args)
scope = args.first || :all
options = args.extract_options!
return User.find(args.first, options) if proxy_owner.admin?
users = []
proxy_owner.sales_regions.collect do |sales_region|
users += sales_region.users
end
users.uniq
end
end
end
Просто чтобы прокомментировать мой комментарий к ответу egarcia, я в итоге решил объявить named_scopes
на моделях с ограниченным доступом. Например:
# app/models/account.rb
class Account < ActiveRecord::Base
named_scope :visible_to, lambda { |user|
return {} if user.can_see_all_accounts?
{ :conditions => ['sales_area_id IN (?)', user.sales_area_ids] }
}
end
# app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
def index
@accounts = Account.visible_to(@current_user)
...
end
end