Как разработать иерархическую систему контроля доступа на основе ролей

Основная сделка заключается в том, что у нас есть специальный «кикстарт» для наших проектов. Для этого мы переделываем пользовательский элемент управления. Я знаю, что есть много вопросов об общем rbac, но я не могу найти ни одного по иерархическому rbac?

Наши требования:

  • Роли могут быть назначены групповым разрешениям
  • Если роль не имеет записи о разрешении, она автоматически отклоняется.
  • Пользователю могут быть предоставлены преимущественные права
  • Пользователи, переопределяющие разрешения, либо предоставляют, либо запрещают
  • Если пользователю явным образом отказано в разрешении, независимо от того, какие роли говорят «предоставлено», преобладает переопределение.
  • Пользователи могут иметь несколько ролей
  • Роли могут иметь иерархию
  • Роли могут наследоваться от других ролей (например, роль «Супермодератор форума» - это «Модератор форума» и «Сопровождение системы», а роль «Модератор форума» уже наследуется от роли «Пользователь форума»)
  • Роли, которые наследуются от другой роли, которая запрещает или предоставляет привилегию, переопределяют их дочернее разрешение
  • Разрешения сгруппированы по «модулю» (например, модуль «Блог» может иметь разрешение «редактировать запись», а модуль «Форум» может иметь разрешение «редактировать запись», и они не будут конфликтовать)
  • Существует разрешение «Все и все», которое автоматически предоставляет полный доступ.

Итак, с учетом этих требований, вот как я собираюсь это сделать.

Таблица: Пользователи

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

Ну, на самом деле это половина, в этой части я уверен, часть, на которой я застреваю, - это иерархические роли.

Итак, как мне это спроектировать? Моя идея состоит в том, чтобы сэкономить на запросах к базе данных, я просто собираюсь построить матрицу разрешений при входе в систему и сохранить ее в сеансе, чтобы запросы не были слишком простыми, поскольку они выполняются только один раз для каждого входа в систему.

Проблема, которую я вижу, заключается в том, что мне нужно знать иерархию ролей, чтобы я мог разрешить разрешения унаследованных ролей, прежде чем разрешить наследование.

Разрешения пользователей - это простая часть, разрешения для пользователей - это, по сути, окончательно решенная группа.


person Hailwood    schedule 22.04.2013    source источник
comment
Есть ли причина, по которой у user нет role, но есть permission? Это образец разрешения, а не образец для подражания, не так ли? При таком подходе роли нигде не используются.   -  person BlitZ    schedule 22.04.2013
comment
Упс, забыл добавить эту таблицу, отредактировал!   -  person Hailwood    schedule 22.04.2013
comment
Тем не менее, я думаю, что нет причин для user иметь permission. В нем могут быть role с permission, но не permission, как они есть. Эта штука портит логику (ИМХО).   -  person BlitZ    schedule 22.04.2013
comment
@CORRUPT, пользователю также могут быть предоставлены разрешения, эти разрешения отменяют любые роли, в которых они находятся. Это избавляет от необходимости создавать совершенно новые роли с одним разрешением только потому, что вы хотите, чтобы пользователь X мог выполнять одно дополнительное действие. Или, если пользователь делал что-то, чего не следовало делать, вы можете быстро отказать этому пользователю в этом. В противном случае вам придется унаследовать роль самого высокого уровня, а затем отказать в ней, что может стать беспорядочным.   -  person Hailwood    schedule 22.04.2013
comment
Это в значительной степени похоже на утверждение, что у каждого пользователя есть своя собственная роль, которая автоматически наследуется от всех остальных ролей, предоставленных пользователю, без необходимости создания группы для пользователя. Имеет ли это смысл?   -  person Hailwood    schedule 22.04.2013


Ответы (1)


Существует способ реализовать наследование ролей с помощью рекурсивного отношения в таблице Roles, сделав ссылку роли на другую запись:

1: n наследование

Это отношение добавит 1 : n наследование в Roles записи. Вы можете получить целое дерево иерархии с помощью этой хранимой функции:

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

Затем вы можете получить все предоставленные разрешения примерно так:

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

Если этого недостаточно, вы можете создать другую таблицу для наследования:

n: m наследование

Но в этом случае потребовался другой алгоритм получения иерархии.


Чтобы решить проблему переопределения, вам необходимо получить разрешения роли и разрешения пользователя. Затем запишите user разрешений вместо roles разрешений в session.


Также я предлагаю удалить grant столбцов в Permission_Role и Permission_User. Нет необходимости сопоставить все разрешения для каждого из них. Достаточно использовать EXISTS запросов: если есть запись, значит разрешение предоставлено, иначе - нет. Если вам нужно получить все разрешения и статусы, вы можете использовать LEFT JOINs.

person BlitZ    schedule 22.04.2013
comment
Звучит хорошо, я изучу это, я определенно смотрю на то, чтобы каждая роль наследовала несколько других ролей, поэтому придется разработать другой алгоритм, а также интересно, как это сделать, если мы не можем создавать хранимые процедуры? Пункт с предоставлением или отказом не в том, чтобы перечислять все разрешения, пункт для предоставления или отклонения заключается в том, что если роль наследует другую роль, если унаследованной роли было предоставлено разрешение, вы можете явно запретить ее в наследующей роли, чтобы отклонить разрешение. - person Hailwood; 22.04.2013