Rails dynamische Findern basierend auf Rolle
-
07-07-2019 - |
Frage
Ich bin auf der Suche nach dem besten Weg saubere Art und Weise zu bauen Rolle / authorisaton-basierten Findern zu bauen?
In meinem Modellschema, ein user
einen von mehreren haben kann (Admin-definiert) Rollen wie Administrator, Regional Manager, Sales Assistant:
Beispiel: ein Benutzer mit einem Regional Manager Rolle gegeben und schloss sich zu einer Region A, würde Ich mag Lage sein, zu fragen, was andere Benutzer sie sehen konnte, z:
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
Danke, sehr zu schätzen jede Hilfe!
Lösung
Ich glaube, Sie müssen an Ort und Stelle eine Autorisierungsmechanismus setzen.
Die beste gem ich für diese wissen, ist declarative_authorization . Ich habe persönlich es in einer Produktionsumgebung verwendet wird, und ich bin damit zufrieden. Es gibt einen Railscast über es auch.
Die Idee ist, dass Sie in einer bestimmten Datei (config/authorization_rules.rb
) die „Rollen und Berechtigungen“ deklarieren. Sie sagen Dinge wie „ein Manager nur die damit verbundenen Clients mit ihm lesen kann“ oder „kann ein Administrator alle Benutzer lesen und schreiben“. In Ihrem Fall würde es so aussehen:
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
Sobald dies festgelegt ist, müssen Sie Ihre Modelle modifizieren, so dass sie declarative_authorization
verwenden. Außerdem wollen wir die user_ids_by_sales_region
Methode definieren
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
Sie müssen auch eine current_user
Methode haben, und einen Weg des aktuellen Benutzers Rolle des Erhaltens (n). Siehe "Die Anforderungen des Plugin Providing" auf der readme .
Dann können Sie tun, was Sie mit with_permissions_to
wollen:
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
Das bedeutet, ein wenig Aufwand am Anfang, aber die Anwendung vereinfacht später stark. Außerdem haben Sie zusätzliche Funktionalitäten, wie versteckt / Teile von Ansichten, die auf den Berechtigungen Aktionen der aktuelle Benutzer hat, sowie das Verbot des Zugriffs auf bestimmte Controller abhängig.
Auch sollte es gut funktionieren mit Seitenzahlen, etc.
Es gibt ein andere deklarative Genehmigung gem genannt Cancan . Ich habe keine Erfahrung mit diesem, aber wenn es von Ryan Bates getan wird, muss es gut sein (er hat eine Railscast es auch). Aber ich glaube nicht, erlaubt es Modellerweiterungen, das ist, was Sie scheinen jetzt zu müssen.
Andere Tipps
Meine Antwort unten ist gut für einfache Sucher; ist es jedoch nicht sehr flexibel und ist nicht kompatibel mit dem will_paginate
Plugin. Kennt jemand einen besseren Weg, um sauber Umfang die Benutzer @current_user
ist in der Lage zu verwalten?
Danke
Nur meine eigene Frage beantwortet, indem die Standardzuordnung Erweiterung überschreiben, wie unten. Es wäre noch toll, wenn Kommentare oder Alternativen zu wissen!
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
Nur meinen Kommentar auf egarcia Antwort folgen zu lassen, ich erklärt named_scopes
auf den eingeschränkten Modellen schließlich nieder. Zum Beispiel:
# 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