Rails динамические искатели, основанные на роли - PullRequest
2 голосов
/ 15 сентября 2009

Я ищу лучший способ создать чистый способ создания искателей на основе ролей / авторизации?

В моей модельной схеме user может иметь одну из нескольких (определяемых администратором) ролей, таких как администратор, региональный менеджер, ассистент по продажам:

Пример Учитывая пользователя с ролью регионального менеджера и присоединенного к региону А, я хотел бы иметь возможность запросить, каких других пользователей она может видеть, например:

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

Спасибо, очень признателен за любую помощь!

Ответы [ 3 ]

2 голосов
/ 03 февраля 2010

Я думаю, вам нужно установить какой-то механизм авторизации.

Лучший драгоценный камень, который я знаю для этого, - 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 ). Тем не менее, я не думаю, что это позволяет расширения модели, а это то, что вам сейчас нужно.

0 голосов
/ 04 февраля 2010

Просто чтобы прокомментировать мой комментарий к ответу 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
0 голосов
/ 15 сентября 2009

Мой ответ ниже подходит для простых искателей; однако, он не очень гибкий и не совместим с плагином 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...