Прямой ответ
RBAC (role-based access control) — модель, в которой права выдаются не конкретному пользователю, а роли, а пользователь получает роль. Это решает практическую задачу: при смене должности или приходе нового сотрудника меняется одна привязка, а не десятки разрешений.
Базовые сущности:
- Subject — кто обращается: пользователь, сервис, API-ключ.
- Role — набор разрешений, соответствующий функции:
support_agent,billing_manager,readonly_analyst. - Permission — право на действие над ресурсом, обычно в форме
действие:ресурс, напримерread:invoice,refund:payment. - Resource — объект или тип объекта: конкретный счёт, все счета, раздел настроек.
Три уровня RBAC по классификации NIST:
- Core. Есть пользователи, роли и разрешения; роль даёт набор разрешений.
- Hierarchical. Роли наследуют разрешения других ролей:
managerвключает всё изsupport_agentплюс свои права. - Constrained. Добавляются ограничения: разделение обязанностей (один человек не может и завести платёж, и одобрить его), взаимоисключающие роли, лимит числа участников роли.
Ключевые принципы проектирования:
- Роли от функций, а не от людей. Роль «Иван» — признак того, что модель сломана. Роль описывает, что человек делает, а не кто он.
- Разрешения позитивные. Модель отвечает на вопрос «что разрешено», а не «что запрещено». Запреты как основа быстро превращаются в непроверяемый набор исключений.
- Проверка в одном месте. Все проверки прав проходят через единую точку (middleware, декоратор, сервис авторизации). Проверки, размазанные по контроллерам, рано или поздно расходятся.
- Проверка на уровне действия, а не страницы. Скрытие кнопки в интерфейсе не является контролем доступа: запрос можно отправить напрямую.
Когда RBAC недостаточно: если доступ зависит от атрибутов объекта и контекста («менеджер видит только своих клиентов», «операция доступна только в рабочее время»), нужна атрибутивная модель либо комбинированная схема «роль + условия», где роль задаёт базовый набор прав, а проверка атрибутов объекта сужает его до конкретной записи.
Практика: схема и проверка
Минимальная схема в PostgreSQL:
CREATE TABLE roles (
id SERIAL PRIMARY KEY,
code TEXT NOT NULL UNIQUE
);
CREATE TABLE permissions (
id SERIAL PRIMARY KEY,
action TEXT NOT NULL, -- read | write | refund | approve
resource TEXT NOT NULL, -- invoice | payment | settings
UNIQUE (action, resource)
);
CREATE TABLE role_permissions (
role_id INT REFERENCES roles(id),
permission_id INT REFERENCES permissions(id),
PRIMARY KEY (role_id, permission_id)
);
CREATE TABLE user_roles (
user_id BIGINT NOT NULL,
role_id INT REFERENCES roles(id),
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
granted_by BIGINT,
PRIMARY KEY (user_id, role_id)
);
Проверка в одном месте:
def require(user, action: str, resource: str) -> None:
if not has_permission(user, action, resource):
audit(user, action, resource, allowed=False)
raise PermissionError(f"{action}:{resource}")
Рекомендации:
- Не плодите роли. Рост числа ролей быстрее числа функций — признак «ролевого взрыва»: под каждый частный случай создаётся отдельная роль. Лечится вводом разрешений и, при необходимости, условий.
- Иерархию используйте осторожно. Глубокая иерархия затрудняет ответ на вопрос «что умеет эта роль». Практика: не больше двух уровней и явная документация наследования.
- Фиксируйте выдачу ролей в журнале. Кто, кому, когда и на основании чего выдал роль — обязательное поле аудита, см. audit log.
- Проверяйте доступ на каждом запросе. Кэширование прав допустимо, но срок жизни кэша должен быть коротким, иначе отозванная роль продолжает работать.
- Сервисы — тоже субъекты. Межсервисные вызовы проверяются по тем же правилам, с отдельными идентификаторами — см. API-ключи.
Практический сценарий: права проверялись в коде и разошлись
Контекст: приложение выросло из прототипа; проверки вида if user.is_admin or user.role == "manager" разбросаны по 40 местам. Поддержка жалуется, что часть операций недоступна, а часть доступна не тем.
- Диагностика. Выписали все проверки из кода и обнаружили три разных определения «менеджера» и два места, где проверяется только
is_admin. Одно место вообще не проверяло права — операция была доступна любому аутентифицированному пользователю. - Проектирование. Составили матрицу «функция → действия»: кто может читать, создавать, изменять, одобрять. На её основе выделили 6 ролей и 23 разрешения в форме
действие:ресурс. - Миграция. Ввели единую функцию проверки и middleware; заменили все точечные условия вызовами этой функции. Для совместимости на время оставили старый флаг
is_adminкак рольadminсо всеми разрешениями. - Проверка. Написали тесты на матрицу: для каждой роли и каждого эндпоинта проверяется ожидаемый код ответа. Такие тесты удобно гонять на Testcontainers с реальной БД.
- Результат. Расхождения исчезли, добавление новой роли перестало требовать правок кода: достаточно записей в таблицах. Проверки остались в одном месте, поэтому ревью прав стало возможным.
Вывод: ценность RBAC не в таблицах, а в единой точке проверки и в том, что роли описывают функции. Модель, которую нельзя объяснить бизнесу одним предложением, спроектирована неверно.
FAQ
Сколько ролей нормально? Ориентир — число различимых функций в продукте, обычно от 3 до 15. Больше двадцати ролей почти всегда означает, что часть «ролей» на самом деле комбинации разрешений, и их стоит выразить через права, а не через новые роли.
Что делать с исключениями для отдельных пользователей? Исключения допустимы, но должны быть явными и ограниченными по времени: отдельная таблица «персональные разрешения» с датой истечения и причиной. Иначе через год никто не помнит, почему у одного пользователя больше прав.
Нужен ли RBAC, если пользователей мало? Если пользователей меньше десяти и все делают одно и то же, достаточно двух ролей: admin и user. Полноценная модель оправдана, когда появляются разные функции и требования к разделению обязанностей.
Как RBAC связан с уязвимостями доступа? Некорректная реализация RBAC — прямой путь к broken access control, одному из наиболее распространённых классов уязвимостей. Что проверять в первую очередь — в статье про OWASP Top 10 глазами разработчика.
Источники: Wikipedia (RBAC), NIST INCITS 359, OWASP — ссылки в шапке.