D'après mon expérience, la vraie question se résume principalement à savoir si une restriction d'accès spécifique à l'utilisateur va se produire ou non.
Supposons, par exemple, que vous conceviez le schéma d'une communauté et que vous autorisiez les utilisateurs à basculer la visibilité de leur profil.
Une option consiste à s'en tenir à un indicateur de profil public/privé et à s'en tenir à des vérifications d'autorisation larges et préventives :'users.view' (voit les utilisateurs publics) contre, disons, 'users.view_all' (voit tous les utilisateurs, pour les modérateurs) .
Une autre implique des autorisations plus raffinées, vous voudrez peut-être qu'ils puissent configurer les choses afin qu'ils puissent se rendre (a) visibles par tous, (b) visibles par leurs amis triés sur le volet, (c) gardés entièrement privés, et peut-être (d ) visible par tous sauf leurs bozos triés sur le volet. Dans ce cas, vous devez stocker des données liées au propriétaire/à l'accès pour des lignes individuelles, et vous devrez fortement abstraire certaines de ces choses afin d'éviter de matérialiser la fermeture transitive d'un graphe dense et orienté.
Quelle que soit l'approche, j'ai constaté que la complexité supplémentaire dans l'édition/l'attribution des rôles est compensée par la facilité/flexibilité qui en résulte dans l'attribution autorisations sur des éléments de données individuels, et que les éléments suivants fonctionnent le mieux :
- Les utilisateurs peuvent avoir plusieurs rôles
- Rôles et autorisations fusionnés dans le même tableau avec un indicateur pour distinguer les deux (utile lors de la modification des rôles/autorisations)
- Les rôles peuvent attribuer d'autres rôles, et les rôles et les autorisations peuvent attribuer des autorisations (mais les autorisations ne peuvent pas attribuer de rôles), à partir du même tableau.
Le graphe orienté résultant peut ensuite être extrait en deux requêtes, construit une fois pour toutes dans un délai raisonnable en utilisant le langage que vous utilisez, et mis en cache dans Memcache ou similaire pour une utilisation ultérieure.
À partir de là, extraire les autorisations d'un utilisateur consiste à vérifier les rôles dont il dispose et à les traiter à l'aide du graphique des autorisations pour obtenir les autorisations finales. Vérifiez les autorisations en vérifiant qu'un utilisateur dispose ou non du rôle/de l'autorisation spécifié(e). Et puis exécutez votre requête/produisez une erreur basée sur cette vérification d'autorisation.
Vous pouvez étendre la vérification à des nœuds individuels (c'est-à-dire check_perms($user, 'users.edit', $node)
pour "peut modifier ce nœud" vs check_perms($user, 'users.edit')
pour "peut modifier un nœud") si vous en avez besoin, et vous aurez quelque chose de très flexible/facile à utiliser pour les utilisateurs finaux.
Comme l'exemple d'ouverture devrait l'illustrer, méfiez-vous de trop vous diriger vers les autorisations au niveau des lignes. Le goulot d'étranglement des performances réside moins dans la vérification des autorisations d'un nœud individuel que dans l'extraction d'une liste de nœuds valides (c'est-à-dire uniquement ceux que l'utilisateur peut afficher ou modifier). Je déconseille quoi que ce soit au-delà des drapeaux et des champs user_id dans les lignes elles-mêmes si vous n'êtes pas (très) versé dans l'optimisation des requêtes.