Elegante Rechteverwaltung

Als Programmierer steht man oft vor dem Problem eine Rechteverwaltung auf die Beine stellen zu müssen. Sei es für einen Webshop, ein CMS oder jegliche andere Form von Memberbereichen. Dieser beitrag soll zeigen, wie man ein performantes und flexibles System mit möglichst wenig Speicherverbrauch umsetzen kann

Eine Idee wäre in der entsprechenden Usertabelle neben Usernamen und Passwort jeweils ein Attribut hinzu zu fügen, was besagt „User darf Beitrag schreiben“, „User darf neues Lesen“ usw.
Überlegen wir uns, welcher Datentyp für dieses Vorhaben in Frage kommt. Da SQL oder Speziell MySQL keinen Booleanschen Datentyp implementiert muss man auf einen Ersatz zurück greifen. Die nächst kleineren wären CHAR(1) oder TINYINT. CHAR(1) hätte den Vorteil einen Wertebereich zu haben, mit dem man viele Eigenschaften sofort abdecken kann, z.B.:

a = „User darf Beitrag schreiben“
b = „User darf Beitrag nicht schreiben“
c = „User darf Beitrag editieren“
d = „User darf Beitrag nicht editieren“
e = „User darf Beitrag lesen“
f = „User darf Beitrag nicht lesen“

Okay, klingt doch gut oder? Was ist aber, wenn der User lesen und editieren darf? Somit würde unser Konzept nicht aufgehen.

Überlegen wir mal weiter. TINYINT ist sicher auch keine Lösung und nimmt 1 Byte ein, wo wir doch nur einen Wahrheitswert speichern wollen – einen booleanschen Wert.

Nehmen wir die Datentypen mal auseinander. TINYINT hat einen Wertebereich von -127 bis 128 (2^7) bzw. von 0 bis 255 (2^8) in der UNSIGNED Version. Diese Zahlen kommen durch 2er Potenzen zustande. Zweierpo…? – wir haben doch 2 Zustände, die wir speichern wollen. „Darf“ oder „Darf nicht“ – „Bit gesetzt“ oder „Bit nicht gesetzt“.

In einem TINYINT können demnach 8*2 solcher oben definierten Rechte gespeichert werden. In einem INT (4 Byte) könnte man demzufolge 32*2 solcher Rechte speichern.

Nur wie bekommt man die Bits gesetzt? Mit Mathematik.

Um ein Bit an Position „n“ zu setzen genügt:
$bit|(1<<$n)

Um ein Bit an Position „n“ zu löschen genügt:
$bit & (~(1<<$n)

Fehlt eigentlich nur noch die Möglichkeit zu Prüfen ob ein Bit gesetzt ist:
$bit & (1<<$n)

Diese Funktion in MySQL als Stored Function implementiert und man hat die Möglichkeit komfortabel ein Rechtesystem aufzubauen:

 CREATE FUNCTION ISBIT(bit INT, n INT)  RETURNS INT  RETURN bit&amp;(1&lt;&lt;n); 
  CREATE FUNCTION SETBIT(bit INT, n INT, init BOOLEAN)  RETURNS INT  RETURN IF(init=1, bit|(1&lt;&lt;n), bit&amp;(~(1&lt;&lt;n))) 
 

Nun noch ein Beispiel zum Setzen und Lesen von Bits:

 
 SELECT ISBIT(Rechte, 3) FROM user WHERE ID=123; 
 UPDATE user SET Rechte=SETBIT(Rechte, 3, 1) 
 WHERE ID=123 

Ich habe mir die Möglichkeit natürlich nicht ausgedacht. Die Rechteverwaltung von Dateisystemen arbeitet auf die selbe Weise.

Die Möglichkeit zwei Zustände zu speichern reicht oft nicht aus. Versteht man die binäre Logik, kann man das System auch erweitern um mehrere Optionen performant verfügbar zu haben bzw. zu speichern.

Bald wird noch ein Beitrag von daryl folgen, wie man ein ähnliches System aufbaut, das aber die Binär-Logik in den PHP-Bereich verlagert. Ähnlich wie es bei UNIX gemacht wird. Dieses System wird wohl aber nicht performanter, maximal etwas verständlicher, denn wie so schon einige Male beschrieben, ist es durchaus schneller, Logik- und Arithmetik-Operationen auf der Datenbank auszuführen.


Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0