0

jFramework 用に NIST レベル 2 RBAC を実装しました。コア SQL は以下のとおりです。

"SELECT COUNT(*) AS Result
    FROM /* Version 2.05 */ 
        `".reg("jf/users/table/name")."` AS TU
    JOIN `".reg("jf/rbac/tables/RoleUsers/table/name")."` AS TUrel ON (TU.`".reg("jf/users/table/UserID")."`=TUrel.`".reg("jf/rbac/tables/RoleUsers/table/UserID")."`)

    JOIN `".reg("jf/rbac/tables/Roles/table/name")."` AS TRdirect ON (TRdirect.`".reg("jf/rbac/tables/Roles/table/RoleID")."`=TUrel.`".reg("jf/rbac/tables/RoleUsers/table/RoleID")."`) 
    JOIN `".reg("jf/rbac/tables/Roles/table/name")."` AS TR ON ( TR.`".reg("jf/rbac/tables/Roles/table/RoleLeft")."` BETWEEN TRdirect.`".reg("jf/rbac/tables/Roles/table/RoleLeft")."` AND TRdirect.`".reg("jf/rbac/tables/Roles/table/RoleRight")."`)
    /* we join direct roles with indirect roles to have all descendants of direct roles */
    JOIN 
    (   `".reg("jf/rbac/tables/Permissions/table/name")."` AS TPdirect 
        JOIN `".reg("jf/rbac/tables/Permissions/table/name")."` AS TP ON ( TPdirect.`".reg("jf/rbac/tables/Permissions/table/PermissionLeft")."` BETWEEN TP.`".reg("jf/rbac/tables/Permissions/table/PermissionLeft")."` AND TP.`".reg("jf/rbac/tables/Permissions/table/PermissionRight")."`)
    /* direct and indirect permissions */
        JOIN `".reg("jf/rbac/tables/RolePermissions/table/name")."` AS TRel ON (TP.`".reg("jf/rbac/tables/Permissions/table/PermissionID")."`=TRel.`".reg("jf/rbac/tables/RolePermissions/table/PermissionID")."`)
    /* joined with role/permissions on roles that are in relation with these permissions*/
    ) ON ( TR.`".reg("jf/rbac/tables/Roles/table/RoleID")."` = TRel.`".reg("jf/rbac/tables/RolePermissions/table/RoleID")."`)
    WHERE 
        TU.`".reg("jf/users/table/UserID")."`=? 
    AND
        TPdirect.{$PermissionCondition}"

これは、特定のユーザーが特定の権限を持っているかどうかを確認し、機能することを目的としています。アプリケーションで非常に頻繁に使用され、大きく依存しているため、最適化できるかどうかを知りたいです。

モデルは次のとおりです。

users : ID,Username,Password
roles : ID,Title,Description, Left , Right (left and right for the nested set model)
permissions : ID,Title,Description , Left , Right
role_permissions : RoleID,PermissionID,AssignmentDate
user_roles : UserID,RoleID

役割と権限のテーブルは両方とも階層的であることに注意してください。したがって、誰かが役割を持っている場合、その人はすべての子孫の役割も持っています。

誰かが権限を持っている場合、その人はすべての子孫権限も持っています。

誰でも私を助けることができますか?私はこれが巨大なものであることを知っています、そして私はそれに大きな賞金をかけるつもりです.

4

1 に答える 1

0

ネストされたセットに固執している場合、以下は役に立ちませんが、隣接リスト方式の使用を検討する場合は、次の簡単な例が興味深いかもしれません。

完全なスクリプトはここにあります: http://pastie.org/1720133

2 つのストアド プロシージャを用意しました。1 つ目は role_id を受け取り、そのロールとその子孫に付与されたアクセス許可のリストを出力します。

mysql> call list_role_permissions(99); -- root role
+---------+---------+-----------+-----------------+
| role_id | perm_id | role_name | permission_name |
+---------+---------+-----------+-----------------+
|      99 |       5 | root      | create forum    |
|       3 |       4 | admin     | move forum      |
|       2 |       3 | moderator | edit post       |
|       1 |       2 | member    | create post     |
|       0 |       1 | guest     | view post       |
+---------+---------+-----------+-----------------+
5 rows in set (0.00 sec)

mysql> call list_role_permissions(2); -- moderator role
+---------+---------+-----------+-----------------+
| role_id | perm_id | role_name | permission_name |
+---------+---------+-----------+-----------------+
|       2 |       3 | moderator | edit post       |
|       1 |       2 | member    | create post     |
|       0 |       1 | guest     | view post       |
+---------+---------+-----------+-----------------+
3 rows in set (0.00 sec)

2 つ目は user_id を取り、そのユーザーのロールに付与された権限のリストを出力します。

mysql> call list_user_role_permissions(1); -- root user
+---------+---------+-----------+-----------------+
| role_id | perm_id | role_name | permission_name |
+---------+---------+-----------+-----------------+
|      99 |       5 | root      | create forum    |
|       3 |       4 | admin     | move forum      |
|       2 |       3 | moderator | edit post       |
|       1 |       2 | member    | create post     |
|       0 |       1 | guest     | view post       |
+---------+---------+-----------+-----------------+
5 rows in set (0.00 sec)

 mysql> call list_user_role_permissions(3); -- moderator user
+---------+---------+-----------+-----------------+
| role_id | perm_id | role_name | permission_name |
+---------+---------+-----------+-----------------+
|       2 |       3 | moderator | edit post       |
|       1 |       2 | member    | create post     |
|       0 |       1 | guest     | view post       |
+---------+---------+-----------+-----------------+
3 rows in set (0.00 sec)

隣接リストのパフォーマンスが気になる場合は、今月初めに行った 500 万行の Yahoo GeoPlanet の例を使用して、このアプローチの結果を確認できます。

MySQL の階層データから深さベースのツリーを生成する (CTE なし)

これが役に立てば幸いです(ネストされたセットのパスで立ち往生している場合は無視してください)

ラグド...

テーブル

drop table if exists roles;
create table roles
(
role_id tinyint unsigned not null primary key,
name varchar(255) unique not null,
parent_role_id tinyint unsigned,
key (parent_role_id)
)engine=innodb;

drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varchar(32) unique not null
)engine=innodb;

drop table if exists user_roles;
create table user_roles
(
user_id int unsigned not null,
role_id tinyint unsigned not null,
primary key (user_id, role_id)
)engine=innodb;

drop table if exists permissions;
create table permissions
(
perm_id smallint unsigned not null auto_increment primary key,
name varchar(255) unique not null
)engine=innodb;

drop table if exists role_permissions;
create table role_permissions
(
role_id tinyint unsigned not null,
perm_id smallint unsigned not null,
primary key (role_id, perm_id)
)engine=innodb;

ストアド プロシージャ

drop procedure if exists list_role_permissions;

delimiter #

create procedure list_role_permissions
(
in p_role_id tinyint unsigned
)
proc_main:begin

declare done tinyint unsigned default 0;
declare dpth smallint unsigned default 0;

create temporary table hier(
 parent_role_id tinyint unsigned, 
 role_id tinyint unsigned, 
 depth smallint unsigned default 0
)engine = memory;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

insert into hier select parent_role_id, role_id, dpth from roles where role_id = p_role_id;
create temporary table tmp engine=memory select * from hier;

while not done do

    if exists( select 1 from roles r inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth) then

    insert into hier select r.parent_role_id, r.role_id, dpth + 1 from roles r
            inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth;

    set dpth = dpth+1;          
        truncate table tmp;
        insert into tmp select * from hier where depth = dpth;
    else
        set done = 1;
    end if;
end while;

select
 rp.*,
 r.name as role_name,
 p.name as permission_name
from
 role_permissions rp
inner join hier h on h.role_id = rp.role_id
inner join permissions p on rp.perm_id = p.perm_id
inner join roles r on rp.role_id = r.role_id
order by
 rp.role_id desc;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end proc_main #

delimiter ;

drop procedure if exists list_user_role_permissions;

delimiter #

create procedure list_user_role_permissions
(
in p_user_id int unsigned
)
proc_main:begin

declare done tinyint unsigned default 0;
declare dpth smallint unsigned default 0;

create temporary table hier(
 parent_role_id tinyint unsigned, 
 role_id tinyint unsigned, 
 depth smallint unsigned default 0
)engine = memory;

/* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */

insert into hier select r.parent_role_id, r.role_id, dpth from roles r 
    inner join user_roles ur on ur.user_id = p_user_id and ur.role_id = r.role_id;

create temporary table tmp engine=memory select * from hier;

while not done do

    if exists( select 1 from roles r inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth) then

    insert into hier select r.parent_role_id, r.role_id, dpth + 1 from roles r
            inner join tmp on r.parent_role_id = tmp.role_id and tmp.depth = dpth;

    set dpth = dpth+1;          
        truncate table tmp;
        insert into tmp select * from hier where depth = dpth;
    else
        set done = 1;
    end if;
end while;

select distinct
 rp.*,
 r.name as role_name,
 p.name as permission_name
from
 role_permissions rp
inner join hier h on h.role_id = rp.role_id
inner join permissions p on rp.perm_id = p.perm_id
inner join roles r on rp.role_id = r.role_id
order by
 rp.role_id desc;

drop temporary table if exists hier;
drop temporary table if exists tmp;

end proc_main #

delimiter ;

テストデータとテスト

insert into roles values 
(99,'root',null),
 (3,'admin',99),
  (2,'moderator',3),
   (1,'member',2),
    (0,'guest',1);

insert into users (username) values 
('f00'),('bar'),('alpha'),('beta'),('gamma');

insert into user_roles values
(1,99), -- (1,1),
(2,3), -- (2,2),(2,1),
(3,2), -- (3,1),
(4,1),
(5,0);

insert into permissions (name) values 
('view post'), ('create post'), ('edit post'), ('move forum'), ('create forum');

insert into role_permissions values 
-- guest
(0,1),
-- member
(1,2),
-- moderator
(2,3),
-- admin
(3,4),
-- root
(99,5);

call list_role_permissions(99); -- root role
call list_role_permissions(3); -- admin role
call list_role_permissions(2); -- moderator role
call list_role_permissions(1); -- member role
call list_role_permissions(0); -- guest role

call list_user_role_permissions(1); -- root user
call list_user_role_permissions(2); -- admin user
call list_user_role_permissions(3); -- moderator user
call list_user_role_permissions(4); -- member user
call list_user_role_permissions(5); -- guest user
于 2011-03-27T00:25:23.930 に答える