Управление доступом в PPEM реализуется моделью RBAC (Role Based Access Control), которая определяет правила разграничения доступа с помощью ролей и прав. Модель устанавливает следующие базовые соглашения:
Объект — ресурс, к которому должен быть предоставлен или ограничен доступ.
Субъект — человек (пользователь) или автоматизированный агент.
Право — разрешение на выполнение операции над объектом.
Роль — рабочая функция или название, которое определяется на уровне авторизации.
Базовые соглашения определяют расширенные соглашения:
Правило доступа — совокупность роли, прав и связей между ними.
Сеанс — соответствие между субъектом и ролью.
Один субъект может иметь несколько ролей.
Одну роль можно назначить нескольким субъектам.
Одна роль может иметь несколько прав.
Одно право можно выдать нескольким ролям.
Права не назначаются субъектам напрямую, а приобретаются субъектами через роли.
Базовые утверждения:
Функциональность PPEM состоит из плагинов.
Каждый плагин позволяет управлять конкретным ресурсом.
Управление ресурсом включает в себя базовые CRUD-операции: создание, просмотр, редактирование, удаление.
Дополнительно управление ресурсом может включать в себя различные RPC-операции, их имена зависят от операции по управлению ресурсом.
Права предоставляют субъектам доступ к различным операциям. Управление любым ресурсом также осуществляется через права (базовый минимум):
РЕСУРС\_create
РЕСУРС\_view
РЕСУРС\_edit
РЕСУРС\_delete
Права включаются в роли.
Роли могут назначаться субъекту как в момент создания субъекта, так и позже.
При получении от субъекта запроса на выполнение операции плагин проверяет право субъекта на доступ к запрошенной операции.
Субъекты (пользователи) могут создавать собственные роли (при наличии прав) и назначать эти роли другим субъектам (пользователям).
Права и роли могут быть объектно-привязанными. Это означает, что можно указать роль и право, разрешающее доступ к ограниченному набору объектов.
Привязка к объекту может осуществляться на уровне роли (все субъекты с этой ролью имеют доступ к объекту) или на уровне пользователя (доступ к объекту имеет только один пользователь).
Субъектами могут выступать:
Пользователи. Роли назначаются пользователям в момент создания. Если роли не указаны явно, может быть назначена роль по умолчанию.
Агенты. Эта роль назначается при создании или регистрации агента.
Объектами могут выступать как ресурсы, так и представления ресурсов в репозитории, например серверы, агенты, экземпляры, пользователи и группы пользователей.
При первом запуске PPEM выполняется инициализация репозитория, при которой заполняются служебные таблицы (права и роли).
При инициализации для каждого плагина устанавливается свой набор прав, ролей и отношений «роль-право». Например, для плагина accounts создаются свои права и роли для управления доступом к объектам этого плагина. Для других плагинов также создаются свои наборы прав и ролей для управления доступом к объектам этих плагинов.
При наличии необходимых прав пользователи могут создавать пользовательские роли, указывать наборы прав, связывать роли с объектами и назначать пользовательские роли субъектам.
Права доступа реализованы следующим набором таблиц, которые принадлежат плагину accounts:
privileges: права с указанием класса объектов, к которым они регулируют доступ.
roles: роли с указанием класса объектов, на которые нацелена роль.
role_privileges: отношение типа «роль-право», устанавливающее связь между ролями и входящими в них правами.
users: пользователи системы (субъекты).
user_roles: отношение типа «пользователь-роль», устанавливающее связь между пользователями и назначенными им ролями.
user_privileges: представление (view), отображающее связи «пользователь-право».
groups: группы системы.
group_roles: отношение типа «группа - роль», устанавливающее связь между группами и назначенными им ролями.
group_users: отношение типа «группа-пользователь», устанавливающее связь между группами и входящими в них пользователями.
privileges #Таблица имеет следующие поля:
id: уникальный идентификатор права.
name: уникальное имя права.
title: описание права.
class: класс объектов, для доступа к которым используется право.
source: имя плагина, который устанавливает право и в дальнейшем осуществляет проверку доступа.
Таблица заполняется менеджером с помощью миграций. Каждый плагин определяет собственный набор прав.
Для проверки прав используется HTTP middleware-обработчик. Создание, изменение и удаление прав пользователем не предусмотрено, так как проверка прав реализуется в коде менеджера.
roles #Таблица имеет следующие поля:
id: уникальный идентификатор роли.
name: уникальное имя роли.
title: описание роли.
class: определяет класс объектов, для доступа к которым используется это право. Значение поля используется в пользовательском интерфейсе как подсказка, чтобы получить права соответствующего класса и список объектов этого класса.
source: имя плагина, который устанавливает роль. В случае пользовательских ролей устанавливается значение user.
Роли создаются менеджером с помощью миграций. Базовый набор ролей устанавливается для плагина accounts.
Пользователь через API менеджера может создавать, изменять и удалять пользовательские роли, но не может изменять или удалять роли, установленные менеджером (системные роли).
role_privileges #Таблица устанавливает связи между ролями и правами и имеет следующие поля:
id: уникальный идентификатор связи.
role: идентификатор роли.
privilege: идентификатор права.
parametric: указывает, используется ли привязка к объекту.
object: уникальный идентификатор объекта произвольного класса.
Класс объекта определяется по значению поля privileges.class. Объявленный здесь идентификатор объекта ограничивает действие права, указанного в role_privileges.privilege, одним объектом и разрешает доступ к объекту только участникам одной роли, указанной в role_privileges.role (доступ разрешён пользователям в user_roles.user, у которых user_roles.role = role_privileges.role).
Комбинация role, privilege и object является уникальным ключом с условием object IS NOT NULL.
Параметризованное объектно-привязанное отношение «роль-право» определяет доступ только к одному объекту (через указание object). Если требуется выдать доступ к N объектам, будет создано N записей в role_privileges.
users #Таблица не имеет полей, имеющих отношение к RBAC. При создании пользователя через API менеджера можно указать список идентификаторов ролей, которые будут назначены пользователю.
user_roles #Таблица устанавливает связи между пользователем и ролями и имеет следующие поля:
id: уникальный идентификатор связи.
user: идентификатор пользователя.
role: идентификатор роли.
object: уникальный идентификатор объекта произвольного класса.
Класс объекта определяется по значению поля privileges.class. Объявленный здесь идентификатор объекта ограничивает действие права, указанной в role_privileges.privilege, одним объектом и разрешает доступ к объекту пользователю, указанному в user_roles.user.
Параметризованное объектно-привязанное отношение «роль-право» определяет доступ только к одному объекту (через указание object). Если требуется выдать доступ к N объектам, будет создано N записей в role_privileges.
user_privileges #Это представление показывает пользователей с ролями и правами и позволяет проверять права конкретного субъекта:
user: user_roles.user
role: user_roles.role
privilege: privilege.name
object: user_roles.objects или role_privileges.object
groups #Таблица не имеет полей, имеющих отношение к RBAC.
При создании группы можно указать список идентификаторов ролей, которые будут назначены группе и, как следствие, всем входящим в неё пользователям.
group_roles #Таблица устанавливает связи между группами и ролями и имеет следующие поля:
group_id: идентификатор группы.
role_id: идентификатор роли.
object: уникальный идентификатор объекта произвольного класса.
Класс объекта определяется по значению поля privileges.class. Объявленный здесь идентификатор объекта ограничивает действие права, указанного в role_privileges.privilege, одним объектом и разрешает доступ к объекту пользователю, указанному в group_roles.role_id.
Параметризованное объектно-привязанное отношение «группа-роль» определяет доступ только к одному объекту (через указание object). При необходимости выдать доступ на N объектов будет создано N записей в group_roles.
group_users #Таблица устанавливает связи между группами и пользователями и имеет следующие поля:
group_id: идентификатор группы.
user_id: идентификатор пользователя.
Отношения «группа-пользователь» не имеют объектных привязок.
В общем случае предполагается, что роли могут включать в себя права, которые разрешают доступ ко всем объектам любого класса.
Для более детального контроля прав доступа в поле role_privileges.object вместе с классом можно также указать идентификатор объекта. Таким образом, действие роли и права будет распространяться только на объект с указанным идентификатором. Объект можно указать в нескольких местах:
Для роли в role_privileges.object, тогда доступ к объекту предоставляется всем членам роли.
Для пользователя в user_roles.object, тогда доступ предоставляется только для одного пользователя.
Для группы в group_roles.object, тогда доступ предоставляется только для пользователей группы.
В процессе аутентификации субъекта создаётся сам сеанс, а также сессионные токены JWT. При создании токена доступа в него вкладывается user_id. Когда пользователь отправляет запросы на выполнение операций, токен доступа добавляется в заголовки запроса.
Менеджер выполняет авторизацию, извлекает из токена доступа user_id пользователя и через роли проверяет наличие права. При наличии права доступ разрешается, если права нет — запрос отклоняется.
Чтобы проверить право субъекта на выполнение операции с объектом, необходимы следующие данные:
user_id или agent_id: идентификатор пользователя или агента.
class: имя класса для объекта (тип ресурса).
object: идентификатор объекта (необязательно).
Клиент в запросе указывает заголовок Authorization: Bearer и прикладывает токен доступа.
Сервер получает запрос и извлекает данные, необходимые для проверки доступа:
Значение user_id (или agent_id) извлекается из токена доступа.
Значение class определяется на основе права.
Значения object:
Для GET-запросов вида /resources извлекаются из URL пути и параметров запроса (ids=?).
Для GET-запросов вида /resources/objectID извлекаются из URL пути.
Для PUT-запросов извлекаются из тела запроса.
Для DELETE-запросов вида /resources извлекаются из тела запросов (отдельное поле в объекте).
Для проверки того, что у пользователя есть конкретная право, обработчику может потребоваться карта идентификаторов операций и соответствующих им прав. Проверка выполняется через репозиторий и представление user_privileges.