web-dev-qa-db-fra.com

Comment concevoir un système de contrôle d'accès basé sur les rôles hiérarchiques

L'accord de base est que nous avons un "kickstart" personnalisé pour nos projets. Pour cela, nous envisageons de refaire le contrôle utilisateur. Je sais qu'il y a beaucoup de questions sur le rbac général, mais je n'en trouve pas sur le rbac hiérarchique?

Nos exigences sont:

  • Les rôles peuvent être attribués aux autorisations de groupe
  • Si le rôle n'a pas d'entrée d'autorisation, il est automatiquement refusé
  • Un utilisateur peut bénéficier d'autorisations prioritaires
  • Un utilisateur remplaçant les autorisations est soit une autorisation soit un refus
  • Si un utilisateur se voit explicitement refuser une autorisation, quel que soit le rôle dit "accordé", le remplacement l'emporte.
  • Les utilisateurs peuvent avoir plusieurs rôles
  • Les rôles peuvent avoir une hiérarchie
  • Les rôles peuvent hériter d'autres rôles (par exemple, un rôle "Forum Super Moderator" est un "Forum Moderator" et un "System Maintainer", et le rôle "Forum Moderator" hérite déjà du rôle "Forum User")
  • Les rôles qui héritent d'un autre rôle qui refusent ou accordent un privilège remplacent leur autorisation enfant
  • Les autorisations sont regroupées par "module" (par exemple, un module "Blog" peut avoir une autorisation "modifier l'entrée" et un module "Forum" peut avoir une autorisation "modifier l'entrée" et il ne se heurtera pas)
  • Il existe une autorisation "Tout et n'importe quoi" qui accorde automatiquement un accès complet

Donc, avec ces exigences à l'écart, voici comment je pense le faire.

Tableau: Utilisateurs

id            | int     | unique id

Tableau: Rôles

id            | int     | unique id
--------------|---------------------------------------------
title         | varchar | human readable name

Tableau: autorisations

id            | int     | unique id
--------------|---------------------------------------------
module        | varchar | module name
--------------|---------------------------------------------
title         | varchar | human readable name
--------------|---------------------------------------------
key           | varchar | key name used in functions

Tableau: Role_User

role_id       | int     | id from roles table
--------------|---------------------------------------------
user_id       | int     | id from users table

Tableau: Permission_Role

id            | int     | unique id
--------------|---------------------------------------------
permission_id | int     | id from permissions table
--------------|---------------------------------------------
role_id       | int     | id from roles table
--------------|---------------------------------------------
grant         | tinyint | 0 = deny, 1 = grant

Tableau: Permission_User

id            | int     | unique id
--------------|---------------------------------------------
permission_id | int     | id from permissions table
--------------|---------------------------------------------
user_id       | int     | id from users table
--------------|---------------------------------------------
grant         | tinyint | 0 = deny, 1 = grant

Eh bien, en fait, c'est la moitié, la partie dont je suis sûr, la partie sur laquelle je suis coincé est les rôles hiérarchiques.

Alors, comment puis-je concevoir cela? Mon idée est que pour économiser sur les requêtes de base de données, je vais simplement créer la matrice des autorisations lors de la connexion et l'enregistrer dans la session afin que les requêtes ne soient pas trop simples car elles ne sont exécutées qu'une seule fois pour chaque connexion.

Le problème que je vois est que, je vais avoir besoin de connaître la hiérarchie des rôles afin de pouvoir résoudre les autorisations de rôles héritées avant de résoudre l'héritage.

Les autorisations utilisateur sont la partie facile, les autorisations par utilisateur sont essentiellement le groupe finalement résolu.

36
Hailwood

Il existe un moyen d'implémenter l'héritage de rôle en utilisant une relation récursive sur la table Roles, en faisant référence au rôle à un autre enregistrement:

1:n inheritance

Cette relation ajoutera 1 : n héritage dans Roles enregistrement. Vous pouvez obtenir toute l'arborescence de la hiérarchie avec cette fonction stockée:

CREATE FUNCTION `getHierarchy`(`aRole` BIGINT UNSIGNED)
RETURNS VARCHAR(1024)
NOT DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE `aResult` VARCHAR(1024) DEFAULT NULL;
DECLARE `aParent` BIGINT UNSIGNED;

SET `aParent` = (SELECT `parent` FROM `Roles` WHERE `id` = `aRole`);

WHILE NOT `aParent` IS NULL DO

    SET `aResult` = CONCAT_WS(',', `aResult`, `aParent`);
    SET `aParent` = (SELECT `parent` FROM `Roles` WHERE `id` = `aParent`);

END WHILE;

RETURN IFNULL(`aResult`, '');
END

Ensuite, vous pouvez obtenir toutes les autorisations accordées avec quelque chose comme ceci:

SELECT
    `permission_id`
FROM
    `Permission_Role`
WHERE
    FIND_IN_SET(`role_id`, `getHierarchy`({$role}))
    AND
    grant;

Si cela ne suffit pas, vous pouvez faire une autre table pour l'héritage:

n:m inheritance

Mais, dans ce cas, il fallait un autre algorithme d'obtention de hiérarchie.


Pour résoudre ( remplacement , vous devrez obtenir des autorisations de rôle et des autorisations d'utilisateur. Ensuite, écrivez user autorisations sur roles autorisations dans session.


Je suggère également de supprimer les colonnes grant dans Permission_Role et Permission_User. Il n'est pas nécessaire de mapper toutes les autorisations pour chacun d'eux. Juste assez pour utiliser les requêtes EXISTS: s'il y a un enregistrement, alors l'autorisation est accordée, sinon - ce n'est pas le cas. Si vous devez récupérer toutes les autorisations et tous les statuts, vous pouvez utiliser LEFT JOINs.

50
BlitZ