Supposons que j'ai un tableau d'articles:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
Maintenant, je veux introduire le concept d '"autorisations" pour chaque élément (veuillez noter que je ne parle pas ici des autorisations d'accès à la base de données, mais des autorisations de logique métier pour cet élément). Chaque élément a des autorisations par défaut et également des autorisations par utilisateur qui peuvent remplacer les autorisations par défaut.
J'ai essayé de réfléchir à plusieurs façons de mettre en œuvre cela et j'ai trouvé les solutions suivantes, mais je ne sais pas laquelle est la meilleure et pourquoi:
1) La solution booléenne
Utilisez une colonne booléenne pour chaque autorisation:
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,
...
);
Les avantages : chaque autorisation est nommée.
Désavantages : il existe des dizaines d'autorisations, ce qui augmente considérablement le nombre de colonnes et vous devez les définir deux fois (une fois dans chaque tableau).
2) La solution entière
Utilisez un entier et traitez-le comme un champ de bits (c'est-à-dire que le bit 0 est pour can_change_description
, le bit 1 est pour can_change_price
, etc., et utilisez des opérations au niveau du bit pour définir ou lire les autorisations).
CREATE DOMAIN permissions AS integer;
Avantages : très rapide.
Inconvénients : vous devez garder une trace de quel bit représente quelle autorisation à la fois dans la base de données et dans l'interface frontale.
3) La solution Bitfield
Identique à 2), mais utilisez bit(n)
. Probablement les mêmes avantages et inconvénients, peut-être légèrement plus lents.
4) La solution Enum
Utilisez un type d'énumération pour les autorisations:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
puis créez une table supplémentaire pour les autorisations par défaut:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
et changez la table de définition par utilisateur en:
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)
);
Avantages : Facile à nommer les autorisations individuelles (vous n'avez pas à gérer les positions des bits).
Désavantages : même lors de la récupération des autorisations par défaut, il nécessite d'accéder à deux tables supplémentaires: premièrement, la table des autorisations par défaut et deuxièmement, le catalogue système stockant les valeurs d'énumération.
Surtout parce que les autorisations par défaut doivent être récupérées pour chaque vue de page unique de cet élément , l'impact sur les performances de la dernière alternative peut être significatif.
5) La solution d'Enum Array
Identique à 4), mais utilisez un tableau pour conserver toutes les autorisations (par défaut):
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
CREATE TABLE items
(
item serial PRIMARY KEY,
granted_permissions permission ARRAY,
...
);
Avantages : Facile à nommer les autorisations individuelles (vous n'avez pas à gérer les positions des bits).
Inconvénients : casse la 1ère forme normale et est un peu moche. Prend un nombre considérable d'octets d'affilée si le nombre d'autorisations est important (environ 50).
Pouvez-vous penser à d'autres alternatives?
Quelle approche adopter et pourquoi?
Remarque: il s'agit d'une version modifiée d'une question publiée précédemment sur Stackoverflow .
la source
bigint
champs (chacun valable pour 64 bits) ou une chaîne de bits. J'ai écrit quelques réponses connexes sur SO qui pourraient être utiles.Réponses:
Je sais que vous ne posez pas de questions sur la sécurité des bases de données en soi , mais vous pouvez faire ce que vous voulez en utilisant la sécurité des bases de données. Vous pouvez même l'utiliser dans une application Web. Si vous ne souhaitez pas utiliser la sécurité de la base de données, les schémas s'appliquent toujours.
Vous souhaitez une sécurité au niveau des colonnes, une sécurité au niveau des lignes et probablement une gestion hiérarchique des rôles. La sécurité basée sur les rôles est beaucoup plus facile à gérer que la sécurité basée sur les utilisateurs.
Cet exemple de code est pour PostgreSQL 9.4, qui sort bientôt. Vous pouvez le faire avec 9.3, mais il faut plus de travail manuel.
Vous voulez que tout soit indexable si vous êtes préoccupé par la performance †, ce que vous devriez être. Cela signifie que les champs de masque de bits et de tableau ne seront probablement pas une bonne idée.
Dans cet exemple, nous conservons les tables de données principales dans le
data
schéma et les vues correspondantes danspublic
.Mettez un déclencheur sur data.thing pour les insertions et les mises à jour en faisant en sorte que la colonne propriétaire soit l'utilisateur_current. Peut-être n'autorisez que le propriétaire à supprimer ses propres enregistrements (un autre déclencheur).
Créez une
WITH CHECK OPTION
vue, ce que les utilisateurs utiliseront réellement. Essayez vraiment de le mettre à jour, sinon vous aurez besoin de déclencheurs / règles, ce qui est plus de travail.Ensuite, créez une table de liste de contrôle d'accès:
Modifiez votre vue pour tenir compte des ACL:
Créez une table de privilèges de ligne par défaut:
Mettez un déclencheur sur insert sur data.thing pour qu'il copie les privilèges de ligne par défaut dans security.thing_acl.
grantor
etadmin_option
colonnes à des tables acl pour suivre qui a accordé le privilège, et si le bénéficiaire peut gérer des privilèges sur cette ligne.† Dans ce cas, pg_has_role n'est probablement pas indexable. Vous devez obtenir une liste de tous les rôles supérieurs à current_user et les comparer à la valeur propriétaire / bénéficiaire à la place.
la source
Avez-vous envisagé d'utiliser la liste de contrôle d'accès extension PostgreSQL de la ?
Il contient le type de données natif PostgreSQL ACE et un ensemble de fonctions qui vous permettent de vérifier si un utilisateur est autorisé à accéder aux données. Il fonctionne soit avec le système de rôles PostgreSQL, soit avec des numéros abstraits (ou UUID) représentant les ID utilisateur / rôle de votre application.
Dans votre cas, vous ajoutez simplement une colonne ACL à vos tables de données et utilisez l'une des
acl_check_access
fonctions pour comparer un utilisateur à une ACL.L'utilisation des listes de contrôle d'accès est un moyen extrêmement flexible de gérer les autorisations de logique métier. De plus, il est incroyablement rapide - le temps système moyen n'est que de 25% du temps nécessaire pour lire un enregistrement. La seule limitation étant qu'il prend en charge un maximum de 16 autorisations personnalisées par type d'objet.
la source
Je peux penser à une autre possibilité de coder cela, la relationnelle
Si vous n'avez pas besoin
permission_per_item
tableyou peut sauter et se connecterPermissions
etItems
directement à laitem_per_user_permissions
table.diagramme de légende
la source