web-dev-qa-db-ja.com

階層的な役割ベースのアクセス制御システムを設計する方法

基本的には、プロジェクト用にカスタムビルドされた「キックスタート」があります。このため、ユーザーコントロールのやり直しを検討しています。一般的なrbacについて多くの質問があることは知っていますが、階層的なrbacについては見つかりませんか?

要件は次のとおりです。

  • 役割をグループ許可に割り当てることができます
  • ロールに許可エントリがない場合、自動的に拒否されます
  • ユーザーにオーバーライド許可を与えることができます
  • アクセス許可をオーバーライドするユーザーは、許可または拒否のいずれかです。
  • どのロールが「許可」と言っても、ユーザーが明示的に許可を拒否された場合、オーバーライドが優先されます。
  • ユーザーは複数の役割を持つことができます
  • ロールは階層を持つことができます
  • 役割は他の役割から継承できます(例:「フォーラムスーパーモデレーター」役割は「フォーラムモデレーター」および「システムメンテナー」であり、「フォーラムモデレーター」役割は既に「フォーラムユーザー」役割から継承しています)
  • 特権を拒否または付与する別のロールから継承するロールは、子のアクセス許可をオーバーライドします
  • 権限は「モジュール」ごとにグループ化されます(たとえば、「ブログ」モジュールには「エントリの編集」権限があり、「フォーラム」モジュールには「エントリの編集」権限があり、衝突しません)
  • 自動的にフルアクセスを許可する「Everything and Anything」権限があります

したがって、これらの要件が邪魔にならないように、ここでそれを行うことを考えています。

表:ユーザー

id            | int     | unique id

表:役割

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

表:許可

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

テーブル:Role_User

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

テーブル: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

テーブル: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

まあ、実際にはそれの半分です、私が確信している部分、私が行き詰まっている部分は階層的な役割です。

だから、これをどのように設計しますか?私の考えは、データベースクエリを保存するには、ログイン時にアクセス許可マトリックスを作成し、セッションに保存するだけです。これにより、クエリがログインごとに1回だけ実行されるため、クエリが単純すぎる必要がなくなります。

問題は、継承を解決する前に、継承されたロールのアクセス許可を解決できるように、ロールの階層を知る必要があるということです。

ユーザー権限は簡単な部分であり、ユーザーごとの権限は基本的に最終的に解決されたグループです。

36
Hailwood

テーブルRolesで再帰リレーションを使用し、ロール参照を別のレコードに作成することにより、ロール継承を実装する方法があります。

1:n inheritance

この関係は、Rolesレコード内に1 : n継承を追加します。次のストアド関数を使用して、階層ツリー全体を取得できます。

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

次に、次のようなすべてのgrantedパーミッションを取得します。

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

十分でない場合は、継承のために別のテーブルを実行できます。

n:m inheritance

しかし、この場合、別の階層取得アルゴリズムが必要でした。


overridingの問題を解決するには、ロール権限とユーザー権限を取得する必要があります。次に、userパーミッションにrolesパーミッションを上書きするsessionパーミッションを書き込みます。


また、Permission_RoleおよびPermission_Usergrant列を削除することをお勧めします。 mapのそれぞれのすべての許可は必要ありません。 EXISTSクエリを使用するのに十分です。レコードがある場合は許可が与えられ、そうでない場合は許可されません。すべての権限とステータスを取得する必要がある場合は、LEFT JOINsを使用できます。

50
BlitZ