Rails Finders dynamiques basés sur le rôle
-
07-07-2019 - |
Question
Je recherche le meilleur moyen de créer un moyen propre de créer des outils de recherche basés sur le rôle / l’autorité?
Dans mon schéma de modèle, un utilisateur
peut avoir un ou plusieurs rôles (définis par l'administrateur), tels que Administrateur, Gestionnaire régional, Assistant commercial:
Exemple Dans le cas d'un utilisateur doté d'un rôle de responsable régional et associé à une région A, j'aimerais pouvoir interroger les autres utilisateurs qu'elle pourrait voir, par exemple:
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
Merci d'apprécier grandement toute aide!
La solution
Je pense que vous devez mettre en place un mécanisme d'autorisation.
Le meilleur bijou que je connaisse à cet égard est déclarative_authorization . Je l'ai personnellement utilisé dans un environnement de production et j'en suis satisfait. Il existe un railscast à ce sujet.
L'idée est que vous déclariez dans un fichier spécifique ( config / autorisation_rules.rb
) les "rôles et autorisations". Vous dites des choses comme "un responsable ne peut lire que les clients qui lui sont associés". ou "un administrateur peut lire et écrire tous les utilisateurs". Dans votre cas, cela ressemblerait à ceci:
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
Une fois que cela est spécifié, vous avez modifié vos modèles afin qu'ils utilisent declarative_authorization
. Définissons également la méthode 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
Vous devez également avoir une méthode current_user
et un moyen d'obtenir le (s) rôle (s) de l'utilisateur actuel. Reportez-vous à la section "Fournir les conditions requises pour le plug-in". section du fichier Lisez-moi .
Vous pouvez ensuite faire ce que vous voulez avec 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
Cela demande un peu d’effort au début, mais simplifie grandement l’application par la suite. Vous disposez également de fonctionnalités supplémentaires, telles que le masquage / l'affichage de parties de vues en fonction des autorisations dont dispose l'utilisateur actuel, ainsi que l'interdiction d'accès à certaines actions du contrôleur.
De plus, cela devrait fonctionner parfaitement avec les paginations, etc.
Il existe un autre joyau d'autorisation déclarative appelé cancan . Je n'ai pas d'expérience avec celui-ci, mais si c'est fait par Ryan Bates, ça doit être bon (il a un railscast ). Cependant, je ne pense pas que cela permette des extensions de modèle, ce dont vous semblez avoir besoin maintenant.
Autres conseils
Ma réponse ci-dessous convient aux chercheurs simples. Cependant, il n'est pas très flexible et n'est pas compatible avec le plugin will_paginate
. Est-ce que quelqu'un connaît un meilleur moyen de gérer proprement les utilisateurs que @ utilisateur_current
est capable de gérer?
Merci
Je viens de répondre à ma propre question, en remplaçant l'extension d'association par défaut comme ci-dessous. Ce serait quand même bien de connaître les commentaires ou les alternatives!
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
Juste pour faire suite à mon commentaire sur la réponse de egarcia, j’ai finalement décidé de déclarer named_scopes
sur les modèles restreints. Par exemple:
# 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