Является ли мой контроль доступа на основе ролей реальным решением?

Я проектирую очень простую систему RBAC (управление доступом на основе ролей) в своем проекте PHP, и, подумав над этим, я нашел решение, но, не зная много о построении бизнес-систем, я не уверен, что есть или были бы какие-то серьезные недостатки дизайна с моим решением.

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

Вот таблица ролей:

# Roles
- id [auto-increment, unsigned]
- role [string, length:50]

# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]

Note: user_id and role_id to be unique index

Проблема, которая беспокоила меня, заключалась в том, что в базе данных не было информации о том, что на самом деле делает эта роль. Но потом я начал думать, было ли это важно. Потому что, если роли имели значимые имена и структуру, то вы могли бы запросить user_roles таблица, чтобы получить все роли пользователей, а затем внутри кода что-то вроде:

# Fetch user with ID 1 from database
$user = User::find(1);

# Fetch the roles the user has from the database
# @returns : Array of roles
$userRoles = $user->roles()

# $userRoles = ['newmember', 'member.post', 'member.chat']

# Can the user send a message?
if(in_array('member.message', $userRoles)
{
# User can send a message
}
else
{
# User can not send a message
}

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

Будет ли мое решение возможным в долгосрочной перспективе?

Спасибо

5

Решение

Вот мой подход к таким системам RBAC: сначала я бы разделил приложение на модули, по крайней мере, логически.

Думайте о модулях как об объектах / ресурсах в вашем приложении, над которыми можно выполнять определенные действия. Ресурсом может быть Пользователь, Членство, Продукт, ..

Давайте предположим, что мы создаем сайт, такой как Stackoverflow, и у нас есть следующие модули:

  • Вопросы (Q&Секция)
  • чат

Каждый из этих модулей регистрируется в таблице базы данных приложений, которая называется modules который может выглядеть так:

# Modules
- id [an application-wide unique module identifier, provided by the module]
- name [string, human readable]

Каждый из этих модулей поставляется с набором действий, которые предварительно определены модулем, например, действия для создания вопроса, удаления пользователей из чатов и т. Д. Эти действия устанавливаются вместе с модулями в таблицу базы данных приложений, которая называется «действия». , который может выглядеть так:

# Actions
- module_id [reference to the modules table, is the namespace for the token]
- action    [string / int, action identifier provided by the module, unique in the scope of the module]
- name [string, human readable name]

- Primary Key: [module_id, action]

Давайте теперь определим модули и действия для нашего клона Stackoverflow:

Modules
+------------------------------------------+
| ID        | Name                         |
+------------------------------------------+
| questions | Question and Answer Module   |
| chat      | Chat Module                  |
+------------------------------------------+

Вместе с модулями будут установлены действия:

Actions
+-----------------------------------------------+
| Module    | Action    |  Name                 |
+-----------------------------------------------+
| questions | read      | Read Questions        |
| questions | create    | Create Questions      |
| questions | edit      | Edit Questions        |
| questions | delete    | Delete Questions      |
| questions | vote      | Vote on Questions     |
|           |           |                       |
| chat      | join      | Join the Chat         |
| chat      | kick      | Kick users            |
| chat      | create    | Create Chatrooms      |
+-----------------------------------------------+

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

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

Давайте начнем с некоторых основных ролей:

Q&A User:
- can read questions
- can create questions
Q&A Moderator:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
Q&A Admin:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
- can delete questions

Chat User:
- can join the chat

Chat Moderator:
- can join the chat
- can kick users from the chat

Chat Admin:
- can join the chat
- can kick users from the chat
- can create chat rooms

Сначала роли создаются в таблице ролей:

# Roles
- id [auto-increment, unsigned]
- name [string, length:50]

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

Roles
+-----------------------+
| ID | Name             |
+-----------------------+
| 1  | Q&A User         |
| 2  | Q&A Moderator    |
| 3  | Q&A Admin        |
| 4  | Chat User        |
| 5  | Chat Moderator   |
| 6  | Chat Admin       |
+-----------------------+

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

Эти назначенные разрешения хранятся в нашей таблице сопоставления. Roles_Actions:

# Roles_Actions
- role_id
- module_id
- action

PK: [role_id, module_id, action]

Заполненный стол:

Roles_Actions
+--------------------------------+
| Role ID | Module ID | Action   |
+--------------------------------+
|    1    | questions |  read    |
|    1    | questions |  create  |
|    2    | questions |  read    |
|    2    | questions |  create  |
|    2    | questions |  vote    |
|    2    | questions |  edit    |
...
|    6    |    chat   |  join    |
|    6    |    chat   |  kick    |
|    6    |    chat   |  create  |
+--------------------------------+

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

- Chuck Norris which is a Q&A Admin and also a Chat Admin (UID = 1)
- Bruce Willis which is a Q&A Moderator and a Chat User   (UID = 2)
- Dilbert which is a Q&A Moderator and a Chat Moderator   (UID = 3)

# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]

Заполненная таблица:

Users_Roles
+---------------------------------+
| User ID | Role ID               |
+---------------------------------+
|    1    |  3 (= Q&A Admin)      |
|    1    |  6 (= Chat Admin)     |
|    2    |  2 (= Q&A Moderator)  |
|    2    |  4 (= Chat User)      |
|    3    |  2 (= Q&A Moderator)  |
|    3    |  5 (= Chat Moderator) |
+---------------------------------+

Таким образом, один пользователь может иметь несколько ролей, и разрешения (пары модуль / действие) затем объединяются на уровне приложения.
Если вы хотите сделать еще один шаг вперед, вы также можете предоставить своего рода наследование в образце для подражания, например, Q&Модератор
может быть определен как дочерний элемент Q&Пользователь, унаследовавший все разрешения от Q&Пользователь и расширение его правами модератора.

Так как же может выглядеть авторизация на прикладном уровне?

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

Дерево разрешений Дилберта:

$dilberts_permissions = array(
"questions" => array("read", "create", "vote", "edit")
"chat"      => array("join", "kick")
)

Для удобства мы создаем простую вспомогательную функцию, такую ​​как:

function has_permission($path, $tree) {
list($module, $action) = explode('.', $path);
return (array_key_exists($module, $tree) && in_array($action, $tree[$module]));
}

В нашем коде теперь мы можем проверить, разрешено ли Дилберту делать определенные вещи с помощью синтаксиса:

has_permission("questions.create", $dilberts_permissions);  // => TRUE
has_permission("questions.delete", $dilberts_permissions);  // => FALSE
has_permission("chat.join", $dilberts_permissions);         // => TRUE
has_permission("chat.create", $dilberts_permissions);       // => FALSE
21

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]