Pergunta

Eu estou procurando a melhor maneira de construir a maneira limpa para papéis de construção localizadores / baseada em authorisaton?

Em meu esquema modelo, um user pode ter um dos vários papéis (admin-definidos), tais como Administrador, Gerente Regional, Assistente de Vendas:

Exemplo Dado um usuário com uma função Gerente Regional e juntou-se a uma Região A, eu gostaria de ser capaz de consulta que outros usuários que podia ver, por exemplo:

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

Obrigado, muito aprecio qualquer ajuda!

Foi útil?

Solução

Eu acho que você precisa colocar no lugar de algum mecanismo de autorização.

A melhor jóia eu sei para isso é declarative_authorization . Eu usei pessoalmente, em um ambiente de produção, e estou satisfeito com ele. Há um railscast sobre isso também.

A idéia é que você declarar em um arquivo específico (config/authorization_rules.rb) as "funções e permissões". Você diz coisas como "um gerente pode lido apenas os clientes associados com ele" ou "um administrador pode ler e escrever todos os usuários". No seu caso, ele ficaria assim:

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

Uma vez que isso for especificado, você tem modificar seus modelos de modo que eles usam declarative_authorization. Além disso, vamos definir o método 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

Você também deve ter um método current_user, e uma maneira de conseguir o papel (s) do usuário atual. Consulte a seção "Fornecendo Requisitos do plug-in" no readme .

Em seguida, você pode fazer o que quiser com 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

Isso significa que um pouco de esforço no início, mas simplifica a aplicação muito mais tarde. Além disso, você tem funcionalidades adicionais, tais como esconder / mostrando peças de pontos de vista, dependendo das permissões do usuário atual, bem como proibindo o acesso a ações de controlador específicos.

Além disso, ele deve funcionar muito bem com paginações, etc.

Há uma outra jóia autorização declarativa chamado cancan . Eu não tenho experiência com este, mas se for feito por Ryan Bates, ele deve ser bom (ele tem um railscast por isso, também). No entanto, eu não acho que ele permite que as extensões do modelo, que é o que você parece precisar agora.

Outras dicas

A minha resposta abaixo é bom para localizadores simples; no entanto, não é muito flexível e não é compatível com o plugin will_paginate. Alguém sabe de uma maneira melhor de âmbito limpa o @current_user usuários é capaz de gerir?

Graças


Apenas respondi minha própria pergunta, substituindo a extensão associação padrão como abaixo. Ainda seria ótimo saber comentários ou alternativas embora!

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

Apenas para acompanhar meu comentário sobre a resposta de egarcia, eu finalmente a acordo sobre declarando named_scopes nos modelos restritos. Por exemplo:

# 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top