Nehmen wir an, ich habe eine Artikeltabelle:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Nun möchte ich das Konzept der "Berechtigungen" für jedes Element einführen (bitte beachten Sie, dass es sich hier nicht um Datenbankzugriffsberechtigungen handelt, sondern um Geschäftslogikberechtigungen für dieses Element). Jedes Element verfügt über Standardberechtigungen und Benutzerberechtigungen, die die Standardberechtigungen überschreiben können.
Ich habe versucht, dies auf verschiedene Arten umzusetzen, und habe die folgenden Lösungen gefunden, bin mir aber nicht sicher, welche die beste ist und warum:
1) Die Boolesche Lösung
Verwenden Sie für jede Berechtigung eine boolesche Spalte:
CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
Vorteile : Jede Berechtigung ist benannt.
Nachteile : Es gibt Dutzende von Berechtigungen, die die Anzahl der Spalten erheblich erhöhen, und Sie müssen sie zweimal definieren (einmal in jeder Tabelle).
2) Die ganzzahlige Lösung
Verwenden Sie eine Ganzzahl und behandeln Sie sie als Bitfeld (dh Bit 0 ist für can_change_description
, Bit 1 ist für can_change_price
usw. und verwenden Sie bitweise Operationen, um Berechtigungen festzulegen oder zu lesen).
CREATE DOMAIN permissions AS integer;
Vorteile : sehr schnell.
Nachteile : Sie müssen sowohl in der Datenbank als auch in der Front-End-Schnittstelle nachverfolgen , welches Bit für welche Berechtigung steht.
3) Die Bitfield-Lösung
Wie 2), jedoch verwenden bit(n)
. Wahrscheinlich die gleichen Vor- und Nachteile, vielleicht etwas langsamer.
4) Die Enum-Lösung
Verwenden Sie einen Aufzählungstyp für die Berechtigungen:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
Erstellen Sie dann eine zusätzliche Tabelle für Standardberechtigungen:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
und ändern Sie die Definitionstabelle pro Benutzer in:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
Vorteile : Einfache Benennung einzelner Berechtigungen (Bitpositionen müssen nicht bearbeitet werden).
Nachteile : Auch wenn Sie nur die Standardberechtigungen abrufen, müssen Sie auf zwei zusätzliche Tabellen zugreifen: erstens auf die Standardberechtigungstabelle und zweitens auf den Systemkatalog, in dem die Enum-Werte gespeichert sind.
Insbesondere, weil die Standardberechtigungen für jede einzelne Seitenansicht dieses Elements abgerufen werden müssen , kann die Leistung der letzten Alternative erheblich beeinträchtigt werden.
5) Die Enum Array Lösung
Entspricht 4), aber verwenden Sie ein Array, um alle (Standard-) Berechtigungen zu speichern:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Vorteile : Einfache Benennung einzelner Berechtigungen (Bitpositionen müssen nicht bearbeitet werden).
Nachteile : Bricht die 1. Normalform und ist etwas hässlich. Nimmt eine beträchtliche Anzahl von Bytes in Folge ein, wenn die Anzahl der Berechtigungen groß ist (ungefähr 50).
Können Sie sich andere Alternativen vorstellen?
Welcher Ansatz sollte gewählt werden und warum?
Bitte beachten Sie: Dies ist eine modifizierte Version einer Frage, die zuvor bei Stackoverflow gepostet wurde .
quelle
bigint
Felder (jedes für 64 Bit) oder eine Bitfolge auswählen. Ich habe ein paar verwandte Antworten auf SO geschrieben, die hilfreich sein könnten.Antworten:
Ich weiß, dass Sie nicht nach der Datenbanksicherheit per se fragen , sondern mit der Datenbanksicherheit tun können, was Sie wollen. Sie können dies sogar in einer Web-App verwenden. Wenn Sie die Datenbanksicherheit nicht verwenden möchten, gelten die Schemas weiterhin.
Sie möchten Sicherheit auf Spaltenebene, Sicherheit auf Zeilenebene und wahrscheinlich hierarchische Rollenverwaltung. Rollenbasierte Sicherheit ist viel einfacher zu verwalten als benutzerbasierte Sicherheit.
Dieser Beispielcode ist für PostgreSQL 9.4, das bald herauskommt. Sie können es mit 9.3 tun, aber es ist mehr Handarbeit erforderlich.
Sie möchten, dass alles indexierbar ist, wenn Sie sich mit der Leistung befassen †, die Sie sein sollten. Dies bedeutet, dass Bitmasken- und Array-Felder wahrscheinlich keine gute Idee sind.
In diesem Beispiel behalten wir die Hauptdatentabellen im
data
Schema und die entsprechenden Ansichten inpublic
.Setzen Sie einen Auslöser für data.thing für Einfügungen und Aktualisierungen, die erzwingen, dass die Eignerspalte der aktuelle_Benutzer ist. Möglicherweise darf nur der Eigentümer seine eigenen Datensätze löschen (ein weiterer Auslöser).
Erstellen Sie eine
WITH CHECK OPTION
Ansicht, die die Benutzer tatsächlich verwenden. Versuchen Sie wirklich, es aktualisierbar zu machen, sonst benötigen Sie Trigger / Regeln, was mehr Arbeit ist.Erstellen Sie als Nächstes eine Zugriffssteuerungslistentabelle:
Ändern Sie Ihre Ansicht, um ACLs zu berücksichtigen:
Erstellen Sie eine Standardreihenberechtigungstabelle:
Setzen Sie einen Trigger auf insert on data.thing, damit die Standardzeilenberechtigungen in security.thing_acl kopiert werden.
grantor
undadmin_option
Spalten hinzuzufügen , um nachzuverfolgen, wer die Berechtigung erteilt hat und ob der Berechtigte Berechtigungen für diese Zeile verwalten kann.† In diesem Fall ist pg_has_role wahrscheinlich nicht indizierbar. Sie müssten eine Liste aller übergeordneten Rollen für current_user abrufen und stattdessen mit dem Eigentümer- / Berechtigungswert vergleichen.
quelle
Haben Sie darüber nachgedacht, die PostgreSQL-Erweiterung für die Zugriffssteuerungsliste zu verwenden ?
Es enthält den nativen PostgreSQL-Datentyp ACE und eine Reihe von Funktionen, mit denen Sie überprüfen können, ob ein Benutzer über die Berechtigung zum Zugriff auf Daten verfügt. Es funktioniert entweder mit dem PostgreSQL-Rollensystem oder mit abstrakten Nummern (oder UUIDs), die die Benutzer- / Rollen-IDs Ihrer Anwendung darstellen.
In Ihrem Fall fügen Sie einfach eine ACL-Spalte zu Ihren Datentabellen hinzu und verwenden eine der
acl_check_access
Funktionen, um einen Benutzer gegen eine ACL zu prüfen.Die Verwendung von ACLs ist eine äußerst flexible Möglichkeit, mit Geschäftslogikberechtigungen umzugehen. Darüber hinaus ist es unglaublich schnell - der durchschnittliche Overhead beträgt nur 25% der Zeit, die zum Lesen eines Datensatzes benötigt wird. Die einzige Einschränkung besteht darin, dass maximal 16 benutzerdefinierte Berechtigungen pro Objekttyp unterstützt werden.
quelle
Ich kann mir eine andere Möglichkeit vorstellen, dies zu kodieren, die relationale
Wenn Sie das nicht brauchen
permission_per_item
kann tableyou es überspringen und verbindenPermissions
undItems
direkt an denitem_per_user_permissions
Tisch.Legende
Diagramm
quelle