Управление ролями (RBAC)

Контроль доступа к ресурсам Meridian через роли. Три встроенные роли с фиксированной матрицей разрешений, защита от self-lockout и инвалидация сессий при смене роли.

Обзор

RBAC (Role-Based Access Control) в Meridian отвечает за авторизацию — что разрешено делать аутентифицированному пользователю. Аутентификация (кто пользователь) — отдельный слой; RBAC решает, какие из эндпоинтов API и разделов панели управления ему доступны.

Каждый пользователь имеет ровно одну роль, и роль определяет матрицу разрешений вида «ресурс × операция». Когда HTTP-запрос приходит на защищённый эндпоинт, middleware authz.Require(resource, operation) сверяет роль пользователя с матрицей; если разрешения нет — возвращается 403 Forbidden.

Ключевые свойства:

  • Три встроенные роли с фиксированными разрешениямиadmin, auditor, user_manager. Создание собственных ролей и редактирование матрицы из UI или API не предусмотрено.
  • Защита от self-lockout — нельзя понизить или удалить последнего администратора, нельзя изменить или удалить собственную учётную запись.
  • Инвалидация сессий при смене роли — после смены роли все сессии пользователя сбрасываются и он вынужден войти заново с новыми разрешениями.

Три роли

РольРазрешения
Admin (admin)Полный набор операций (View, Read, Create, Update, Delete, Download) на всех ресурсах.
Auditor (auditor)Только чтение (View, Read, Download) на всех ресурсах. Не может ничего изменять.
User Manager (user_manager)Полный набор операций на Customers, Teams, VirtualKeys, Governance. Только чтение на остальных ресурсах. Доступ к ресурсу RBAC отсутствует — User Manager не может управлять ролями других пользователей.
Список ролей в панели управления

Роли соответствуют типичным ролям enterprise-среды: Admin — для платформенной команды, Auditor — для compliance / security, User Manager — для команды governance, которая управляет квотами и виртуальными ключами без доступа к платформенным настройкам.

Матрица разрешений

Разрешение — это пара «ресурс × операция». Шесть операций:

ОперацияНазначение
ViewДоступ к UI-разделу (видимость пункта меню, страницы).
ReadЧтение данных через API (GET-эндпоинты).
CreateСоздание нового объекта (POST).
UpdateИзменение существующего объекта (PUT/PATCH).
DeleteУдаление объекта (DELETE).
DownloadЭкспорт данных (например, скачивание лога).

Список ресурсов определяется в framework/authz/authz.go. Часть ресурсов скрывается feature-flag'ами: на этапе сборки можно отключить видимость отдельных подсистем (например, MCP Gateway, Plugins, Adaptive Routing, Audit Logs, Guardrails, User Provisioning, Prompt Deployments). API ответ GET /api/rbac/roles возвращает только включённые ресурсы — FilterPermissions отбрасывает выключенные перед отдачей в UI.

Базовые ресурсы (в текущей сборке):

Cluster, Settings, Users, Logs, Observability, VirtualKeys, ModelProvider, Customers, Teams, RBAC, Governance, RoutingRules, PIIRedactor, PromptRepository.

Если в вашей сборке включён, например, EnableMCPGateway, в матрице появится ресурс MCPGateway. Если выключен — этот ресурс не будет виден ни в API, ни в UI, и middleware authz.Require(ResourceMCPGateway, ...) всё равно отработает корректно (проверка ведётся по строковой константе ресурса в коде).

Назначение роли пользователю

  1. Перейдите в Governance → Roles & Permissions.
  2. Раскройте нужную роль — увидите список её пользователей.
  3. Напротив пользователя нажмите выпадающий список и выберите новую роль.
  4. После сохранения сессии этого пользователя инвалидируются — он войдёт заново с обновлёнными правами.
Назначение роли пользователю в панели управления

В панели управления нет создания собственных ролей и нет редактирования матрицы разрешений — роли и их разрешения зашиты в коде.

curl -X PUT http://localhost:8080/api/users/{user_id}/role \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin_token>" \
  -d '{
    "role": "auditor"
  }'

Допустимые значения role: admin, auditor, user_manager.

Возможные ошибки:

КодПричина
400 Bad RequestНеизвестное значение role, попытка изменить собственную роль, попытка понизить последнего admin.
404 Not FoundПользователь с таким ID не найден.
403 ForbiddenУ вызывающего нет права Update на ресурсе RBAC (или Users).

Безопасность

Запрет понижения последнего admin'а. Сервер вычисляет количество пользователей с ролью admin под FOR UPDATE-локом и отказывает в смене роли, если демоция оставит систему без admin'ов. Тот же лок защищает от TOCTOU-гонки между двумя одновременными запросами понижения.

Запрет смены и удаления собственной учётной записи. API отвергает PUT /api/users/{id}/role и DELETE /api/users/{id}, если в качестве id передан вызывающий пользователь. Это защита от случайного блокирования доступа к собственному кабинету.

Инвалидация сессий при смене роли. После успешного PUT /api/users/{id}/role все сессии этого пользователя удаляются — следующий запрос от него вернёт 401 Unauthorized, и он переаутентифицируется. Без этого старые токены продолжали бы давать доступ по старой роли.

Поведение при отключённой аутентификации. Если в запросе нет роли (auth полностью выключена), middleware authz.Require пропускает запрос дальше: за блокировку неаутентифицированных запросов отвечает auth-middleware, а не authz. Если роль установлена в пустую строку — это считается некорректным состоянием и запрос отвергается с 403 Forbidden.

Управление пользователями

Список пользователей:

curl http://localhost:8080/api/users

Получить пользователя:

curl http://localhost:8080/api/users/{user_id}

Создать пользователя:

curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice.smith",
    "display_name": "Alice Smith",
    "password": "S3cure-Passw0rd!",
    "role": "user_manager"
  }'

Требования к полям:

ПолеТребования
usernameТолько буквы, цифры, дефис, подчёркивание и точка. Не более 255 символов. Уникален.
passwordМинимум 8 символов. Хешируется на сервере.
roleadmin, auditor или user_manager.
display_nameОпционально; если не указан — берётся username.

Изменить отображаемое имя:

curl -X PUT http://localhost:8080/api/users/{user_id} \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Alice S."
  }'

PUT /api/users/{id} меняет только display_name. Смена username или password через этот эндпоинт не поддерживается.

Смена собственного пароля:

curl -X PUT http://localhost:8080/api/users/me/password \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <user_token>" \
  -d '{
    "current_password": "old-password",
    "new_password": "new-S3cure-Passw0rd!"
  }'

Удалить пользователя:

curl -X DELETE http://localhost:8080/api/users/{user_id}

Удаление защищено теми же правилами, что и смена роли: нельзя удалить себя, нельзя удалить последнего admin'а.

Получить список ролей и их матрицу

curl http://localhost:8080/api/rbac/roles

Ответ возвращает три роли, в каждой — описание, матрицу разрешений (после feature-flag-фильтрации) и список присвоенных пользователей:

{
  "roles": [
    {
      "name": "admin",
      "display_name": "Admin",
      "description": "Full control of everything",
      "permissions": {
        "Cluster": ["View", "Read", "Create", "Update", "Delete", "Download"],
        "VirtualKeys": ["View", "Read", "Create", "Update", "Delete", "Download"],
        "Users": ["View", "Read", "Create", "Update", "Delete", "Download"]
      },
      "users": [
        {
          "id": "550e8400-e29b-41d4-a716-446655440000",
          "username": "alice.smith",
          "display_name": "Alice Smith"
        }
      ]
    },
    {
      "name": "auditor",
      "display_name": "Auditor",
      "description": "Read-only access to all resources",
      "permissions": { "...": "..." },
      "users": []
    },
    {
      "name": "user_manager",
      "display_name": "User Manager",
      "description": "Manage customers, teams, virtual keys, and governance; read-only access to other resources",
      "permissions": { "...": "..." },
      "users": []
    }
  ]
}

Эта ручка нужна UI для отрисовки страницы Roles & Permissions и используется как справочник для самостоятельной интеграции с внешними панелями (например, чтобы понять, кто из пользователей сейчас имеет роль admin).

Содержание