Neulich habe ich im Buch "Agile Web Development with Rails" eine ganz nette Sache entdeckt, die mir für die Benutzer-Rechteverwaltung sehr gelegen kommt. Ich habe die Entitäten User
, Group
und AccessRight
, wobei sowohl Benutzer als auch Gruppen beliebig viele Zugriffsrechte haben können (habtm). Üblicherweise würde man zur Erstellung der Rechte-Beziehungen einfache Join-Tables mit zwei Fremdschlüsseln verwenden (users_access_rights(user_id, access_right_id)). Da zusätzlich aber gespeichert werden soll, wer wann von wem welche Rechte zugewiesen bekommen hat, macht es Sinn, eine zusätzliche Entität UserAccessRight
(dito für Gruppen) zu erstellen.
Klassischerweise würde das dann so aussehen:
class User < ActiveRecord::Base
belongs_to :user_access_right
end
# dito für Group...
class AccessRight < ActiveRecord::Base
belongs_to :user
end
class UserAccessRight < ActiveRecord::Base
has_one :user
has_one :access_right
end
# Liste alle Rechte von Benutzer 1 auf:
user = User.find 1
user.user_access_rights.each do |acc|
p acc.right.name
end
# darf der Benutzer also Benutzer hinzufügen?
if user.user_access_rights.find {|r| r.access_right_name == 'add_user'}
p "Benutzer #{user.name} darf Benutzer hinzufügen."
end
Das funktioniert zwar wunderbar, aber bei genauerem hinsehen ist es bestenfalls "etwas unhübsch", um immer über die UserAccessRight
-Entität zuzugreifen, wenn man eigentlich mit dem AccessRight
selbst arbeiten möchte -- denn eigentlich soll ja weiterhin lediglich eine HABTM-Beziehung zwischen User und AccessRight bestehen, die aber so aufgelöst wird.
Dafür gibt's aber in Rails die folgende Lösung:
class User < ActiveRecord::Base
has_many :user_access_rights
has_many :access_right,
:through => :user_access_right
end
class AccessRight < ActiveRecord::Base
has_many :user_access_rights
has_many :access_rights,
:through => :user_access_rights,
:unique => true
end
class UserAccessRight < ActiveRecord::Base
belongs_to :user
belongs_to :access_right
end
# Liste alle Rechte von Benutzer 1 auf:
user = User.find 1
user.rights.each do |acc|
p acc.name
end
# darf der Benutzer also Benutzer hinzufügen?
if user.rights.find {|r| r.name == 'add_user'}
p "Benutzer #{user.name} darf Benutzer hinzufügen."
end
So kann man also direkt, wie bei einer klassischen HABTM-Beziehung, auf die Rechte eines Benutzers zugreifen. Zusätzlich stehen aber auch unmittelbar die Meta-Informationen zu den einzelnen Rechten (wann und von wem verliehen) zur Verfügung.
Also ich finde das toll ;-)
Als Nächstes muss ich nur mal sehen, wie man halbwegs elegant an die Rechte herankommt, die einem Benutzer über seine Gruppenzugehörigkeiten verliehen werden. Zur Not tut's natürlich eine einfache Suchschleife über alle Gruppen und deren Rechte, oder die Rechte werden bei der Objektinitialisierung geladen, oder alles wird in einer nifty-tricky-magic Funktion gekapselt mit deren Innenleben sich lieber keiner auseinandersetzen sollte ;-)
Comments (1)
Ich glaube, bei Black habe ich die Empfehlung gelesen, in solchen Fällen zunächst die "einfache" Lösung zu implementieren, die das ganze verständlich in Ruby löst, bevor man sie nachher durch kluges SQL löst. Genau danach riecht es hier auch ... Du weißt ja: "Premature optimization is the root of all evil" ;-)
Posted by Stefan Tilkov | October 19, 2007 7:15 AM
Posted on October 19, 2007 07:15