角色基于访问控制的递归SQL查询

时间:2018-02-23 18:45:19

标签: sql sqlite rbac

我创建了以下基于角色的访问数据库模式,该模式能够保存角色,操作和类型。角色可以对类型执行特定操作。与用户或类型的连接在这里并不重要,因为这将是特定于应用程序的。这三个表中的每一个都可以拥有他们想要的父母。

目前我正在努力解决一个问题,该问题从role_operation_type table输出所有可能的组合。

每个角色都应该继承祖先的每个类型的权限,这些权限可以是多个。在我看来,我需要三个嵌套递归查询,或者有更快的方法来实现它?

我的目的是将该查询放在视图中,并在用户请求对类型进行操作时选择所需的值。

以下是数据库架构:

CREATE TABLE IF NOT EXISTS `role` (
 `id` INTEGER PRIMARY KEY,
 `name` VARCHAR NOT NULL UNIQUE
);

CREATE TABLE IF NOT EXISTS `role_role` (
 `role_id` INTEGER NOT NULL REFERENCES `role`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 `parent_role_id` INTEGER NOT NULL REFERENCES `role`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 CONSTRAINT role_uq UNIQUE (`role_id`, `parent_role_id`),
 CONSTRAINT role_chk CHECK(`role_id` != `parent_role_id`)
);

CREATE TABLE IF NOT EXISTS `operation` (
 `id` INTEGER PRIMARY KEY,
 `name` VARCHAR NOT NULL UNIQUE
);

CREATE TABLE IF NOT EXISTS `operation_operation` (
 `operation_id` INTEGER NOT NULL REFERENCES `operation`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 `parent_operation_id` INTEGER NOT NULL REFERENCES `operation`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 CONSTRAINT operation_uq UNIQUE (`operation_id`, `parent_operation_id`),
 CONSTRAINT operation_chk CHECK(`operation_id` != `parent_operation_id`)
);

CREATE TABLE IF NOT EXISTS `type` (
 `id` INTEGER PRIMARY KEY,
 `name` VARCHAR NOT NULL UNIQUE
);

CREATE TABLE IF NOT EXISTS `type_type` (
 `type_id` INTEGER NOT NULL REFERENCES `type`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 `parent_type_id` INTEGER NOT NULL REFERENCES `type`(`id`) ON UPDATE CASCADE ON DELETE CASCADE,
 CONSTRAINT type_uq UNIQUE (`type_id`, `parent_type_id`),
 CONSTRAINT type_chk CHECK(`type_id` != `parent_type_id`) 
);



CREATE TABLE IF NOT EXISTS `role_operation_type` (
 `role_id` INTEGER NOT NULL REFERENCES `role`(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 `operation_id` INTEGER NOT NULL REFERENCES `operation`(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 `type_id` INTEGER NOT NULL REFERENCES `type`(`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT role_id_operation_id_type_id_uq UNIQUE (`role_id`, `operation_id`, `type_id`)
);


CREATE VIEW IF NOT EXISTS role_role_recursive_view AS
WITH RECURSIVE p(role_id, r, parent_role_id) AS (
    SELECT ROLE.id, ROLE.id, role_role.parent_role_id
    FROM ROLE
    INNER JOIN role_role ON ROLE.id = role_role.role_id

    UNION

    SELECT p.r, p.role_id, role_role.parent_role_id
    FROM p
    INNER JOIN role_role ON p.parent_role_id = role_role.role_id
    WHERE p.r != role_role.parent_role_id
)
SELECT p.role_id, p.parent_role_id FROM p ORDER BY role_id;

CREATE VIEW IF NOT EXISTS operation_operation_recursive_view AS
WITH RECURSIVE o(operation_id, o, parent_operation_id) AS (
    SELECT operation.id, operation.id, operation_operation.parent_operation_id
    FROM operation
    INNER JOIN operation_operation ON operation.id = operation_operation.operation_id

    UNION

    SELECT o.o, o.operation_id, operation_operation.parent_operation_id
    FROM o
    INNER JOIN operation_operation ON o.parent_operation_id = operation_operation.operation_id
    WHERE o.o != operation_operation.parent_operation_id
)
SELECT o.operation_id, o.parent_operation_id FROM o ORDER BY operation_id;

CREATE VIEW IF NOT EXISTS type_type_recursive_view AS
WITH RECURSIVE t(type_id, t, parent_type_id) AS (
    SELECT TYPE.id, TYPE.id, type_type.parent_type_id
    FROM TYPE
    INNER JOIN type_type ON TYPE.id = type_type.type_id

    UNION

    SELECT t.t, t.type_id, type_type.parent_type_id
    FROM t
    INNER JOIN type_type ON t.parent_type_id = type_type.type_id
    WHERE t.t != type_type.parent_type_id
)
SELECT t.type_id, t.parent_type_id FROM t ORDER BY type_id;

1 个答案:

答案 0 :(得分:1)

现在我将用一个效果很好的解决方案回答我自己的问题。这是一个递归查询,并生成每个可能的组合。这是一个递归查询,当继承级别变得更深时可能会很慢。

        CREATE VIEW IF NOT EXISTS role_operation_type_recursive_view AS
        WITH RECURSIVE T(role_id, operation_id, type_id) AS (
            WITH RECURSIVE O(role_id, operation_id, type_id) AS (
                WITH RECURSIVE R(role_id, operation_id, type_id) AS (
                    SELECT role_id, operation_id,type_id
                    FROM role_operation_type

                    UNION

                    SELECT role_role_recursive_view.role_id, R.operation_id, R.type_id
                    FROM R
                    INNER JOIN role_role_recursive_view ON R.role_id = role_role_recursive_view.parent_role_id
                )
                SELECT * FROM R

                UNION

                SELECT O.role_id,operation_operation_recursive_view.parent_operation_id ,O.type_id
                FROM O
                INNER JOIN operation_operation_recursive_view ON O.operation_id = operation_operation_recursive_view.operation_id
            )
            SELECT * FROM O

            UNION

            SELECT T.role_id, T.operation_id, type_type_recursive_view.type_id
            FROM T
            INNER JOIN type_type_recursive_view ON T.type_id = type_type_recursive_view.parent_type_id
        )
        SELECT * FROM T;