Перейти к содержанию

Модель безопасности

Provisa применяет многоуровневую модель безопасности для каждого языка запросов (GraphQL, SQL, Cypher) и каждого транспорта (REST, gRPC, Arrow Flight, JDBC, WebSocket). (REQ-001, REQ-266) Governance применяется единообразно — не существует пути запроса, который бы его обходил. (REQ-002, REQ-266)

Уровни применяются по порядку. Запрос должен пройти каждый уровень, прежде чем будет оценён следующий.

Многоуровневая модель

Уровень 0 — Фильтрация интроспекции

Схема и каталог, представленные роли, содержат только таблицы из её списка domain_access и столбцы, прошедшие правила visible_to для каждого столбца. (REQ-039) Объекты вне доступа роли невидимы на этапе обнаружения — их нельзя запросить, автодополнить или вывести их существование. (REQ-039) Это применяется к схеме GraphQL, каталогу SQL и браузеру схемы редактора запросов. (REQ-039, REQ-363)

См. Видимость схемы.

Уровень 1 — Публичный доступ

Таблицы в доменах без ограничения domain_access видны всем аутентифицированным identity без дополнительной конфигурации. Нулевое трение для по-настоящему публичных данных.

Уровень 2 — Доступ к домену

Каждая роль несёт список domain_access с идентификаторами доменов. Запрос, затрагивающий таблицу вне этих доменов, отклоняется до выполнения. (REQ-038, REQ-039) Это грубая граница владения — роль HR не может достичь таблиц финансов независимо от того, как написан SQL. (REQ-002)

См. Модель прав.

Уровень 3 — Безопасность на уровне строк

После подтверждения доступа к домену, предложения WHERE для каждой таблицы и роли внедряются в каждый SELECT во время выполнения. (REQ-041, REQ-263) Предложения оцениваются относительно сырых данных. Региональный менеджер, запрашивающий общую таблицу заказов, видит только строки своего региона даже при SELECT *. (REQ-264)

См. Безопасность на уровне строк (RLS).

Уровень 4 — Видимость и маскирование столбцов

Столбцы со списком visible_to, исключающим запрашивающую роль, удаляются из вывода запроса. (REQ-040, REQ-263) Столбцы с правилом маскирования получают заменённые значения — редактирование по regex, замена константой или усечение — прежде чем результаты покинут сервер. (REQ-263) Маскирование применяется во всех языках запросов и форматах вывода. (REQ-263)

См. Модель разрешений столбцов и Маскирование на уровне столбцов.

Уровень 5 — Защита предикатов

Замаскированные столбцы отклоняются из предложений WHERE и HAVING. (REQ-263) Без этого вызывающая сторона могла бы вывести немаскированное значение, бинарно перебирая его в фильтре, даже если вывод замаскирован. Отклонение применяется на этапе разбора запроса, до выполнения. (REQ-531)

Governance связей (V002)

Условия JOIN в SQL должны соответствовать зарегистрированной, утверждённой связи между таблицами. (REQ-001) Неутверждённые соединения отклоняются. Каждая связь несёт человекочитаемую причину и описание — руководство как для пользователей, так и для автономных агентов о том, почему существует путь обхода. Это политика governance, а не жёсткая граница безопасности: уровни 2–5 действуют независимо от структуры соединения, поэтому намеренный обход не раскрывает данные, которые роль не могла бы получить через два отдельных запроса. Попытки обхода регистрируются и подлежат аудиту.

Механизмы обхода — V002 можно обойти двумя способами. Первый — это возможность: роль, обладающая ignore_relationships, соединяет отношения, которые каталог не покрывает. Среди засеянных системных ролей ею обладает только modeler — роль исследования, задача которой определять модель, а не применять её. (REQ-1297) У analyst её нет. [tool-verified: provisa/core/db.py:84]

Второй — это отказ по двум условиям, оба из которых должны выполняться:

  1. Флаг ролиrelationship_guard: false в определении роли (по умолчанию: true). [tool-verified: provisa/core/models.py:349]
  2. Отказ для отдельного запроса — SQL содержит комментарий --relationship-guard=false. [tool-verified: provisa/compiler/params.py:80]

Один только флаг роли не обходит V002; один только комментарий не обходит V002.

Режим высокой безопасности фиксирует защиту. При security.mode: high не действует ни один из обходов: ignore_relationships игнорируется, relationship_guard: false игнорируется, и каждое соединение должно присутствовать в каталоге утверждённых связей. (REQ-693) Это намеренное дублирование — производственная роль, которой возможность выдали по ошибке, всё равно не сможет выйти за пределы модели. [tool-verified: provisa/pgwire/_pipeline.py:377]

Путь GraphQL — V002 безусловно пропускается для запросов GraphQL. Связи, определённые в SDL, предварительно утверждены по замыслу; проверка избыточна и не применяется. [tool-verified: provisa/api/data/endpoint.py:468]

Пути SQL и Cypher — V002 активен по умолчанию. И endpoint_dev.py, и cypher_router.py применяют проверку из двух условий перед вызовом validate_sql. [tool-verified: provisa/api/data/endpoint_dev.py:127, provisa/api/rest/cypher_router.py:260]

Путь pgwire — та же проверка из двух условий, что и для SQL. Комментарий --relationship-guard=false удаляется из запроса перед выполнением; он не доходит до базы данных. [tool-verified: provisa/pgwire/_pipeline.py:60]


Эти уровни компонуются. Роль с доступом к домену, RLS и замаскированными столбцами имеет все пять ограничений активными одновременно. Добавление нового источника данных, столбца или связи не требует обновления каждого правила — каждый уровень настраивается независимо и применяется автоматически к любому запросу, затрагивающему управляемые объекты.


Модель прав

Независимо назначаемые возможности с опциональной иерархией ролей через parent_role_id. admin предоставляет всё. (REQ-042)

Возможность Описание
source_registration Регистрация источников данных
table_registration Регистрация таблиц, столбцов
create_relationship Определение связей FK
access_config Настройка RLS, маскирования
query_development Выполнение запросов
write Вызов зарегистрированных мутаций (грубые врата; см. Авторизация мутаций)
full_results Обход ограничений выборки
ignore_relationships Обход governance связей (V002). Среди системных ролей ею обладает только modeler, и в режиме высокой безопасности она игнорируется полностью
admin Суперпользователь — предоставляет всё

Наследование ролей

Роли могут наследовать возможности и доступ к домену от родительской роли через parent_role_id. (REQ-215) Иерархия сплющивается при запуске — дочерние роли объединяют возможности и доступ к домену родителя со своими собственными. (REQ-215)

roles:
  - id: basic_user
    capabilities: [query_development]
    domain_access: [public]
  - id: analyst
    capabilities: [full_results]
    domain_access: [sales, analytics]
    parent_role_id: basic_user   # inherits query_development + public domain

Модель разрешений столбцов

Каждый столбец имеет четырёхпольную модель разрешений, управляющую доступом к чтению, записи и маскированием для каждой роли. (REQ-042, REQ-249)

Трёхуровневая видимость

Уровень Условие Результат
Скрыт Роль не в visible_to Столбец отсутствует в SDL GraphQL
Замаскирован Роль в visible_to, есть правило маскирования, роль не в unmasked_to Столбец виден, но данные замаскированы в SQL
Немаскирован Роль в visible_to И роль в unmasked_to (или нет правила маскирования) Полный доступ на чтение

Разрешения на запись

Поле Пусто означает Назначение
visible_to Все роли могут читать Управляет тем, кто видит столбец (замаскированный или нет)
unmasked_to Ни одна роль не видит немаскированное значение Управляет тем, кто обходит маскирование
writable_by Ни одна роль не может писать Управляет тем, кто может изменять (INSERT/UPDATE)

Разрешение на запись применяется в конвейере мутаций. Роль, не входящая в writable_by, получает ошибку 403 при попытке записи в ограниченный столбец. (REQ-033, REQ-034)

Пример

columns:
  - name: email
    visible_to: [admin, analyst, viewer]
    writable_by: [admin]
    unmasked_to: [admin]
    mask_type: regex
    mask_pattern: "(.).*@"
    mask_replace: "$1***@"
  - name: salary
    visible_to: [admin, hr]
    writable_by: [hr]
    unmasked_to: [admin, hr]
    mask_type: constant
    mask_value: "0"
  - name: created_at
    visible_to: []           # all can read
    writable_by: []          # nobody can write (auto-set)

В этом примере:

  • email: admin видит [email protected] и может редактировать; analyst/viewer видят a***@example.com
  • salary: admin и hr видят реальное значение; hr может редактировать; все остальные роли вообще не видят столбец
  • created_at: все могут читать, никто не может писать

Авторизация мутаций

Зарегистрированные мутации (удалённые GraphQL, OpenAPI, gRPC, Hasura) защищены двумя независимыми проверками. (REQ-867, REQ-868) Роль может вызвать мутацию, только если она обладает глобальной возможностью write И присутствует в списке writable_by этой мутации. (REQ-868) Пустой writable_by означает запрет по умолчанию — ни одна роль не может её вызвать. (REQ-867)

Мутации классифицируются как записи по контракту, а не по объявлению вызывающей стороны. (REQ-869) SELECT, ссылающийся на функцию типа мутация, повышается до записи и подлежит той же двухврательной проверке, поэтому вызывающая сторона не может вызвать мутацию, замаскировав её под чтение. (REQ-869) Переклассификация мутации в безопасную для чтения требует возможности access_config и записывается как решение governance; отказа для отдельного запроса не существует. (REQ-870)

Видимость схемы

Схемы GraphQL для каждой роли скрывают неавторизованное содержимое: (REQ-039)

  • Доступ к домену: роль видит таблицы только в своих доменах domain_access ("*" = все) (REQ-039)
  • Видимость столбцов: столбцы, не входящие в visible_to для роли, опускаются из SDL (REQ-039)
  • Неавторизованные таблицы/столбцы не появляются в схеме (REQ-039)

Безопасность на уровне строк (RLS)

Внедрение предложения SQL WHERE для каждой таблицы и роли. Применяется после компиляции, до выполнения. (REQ-041, REQ-263)

rls_rules:
  - table_id: orders
    role_id: analyst
    filter: "region = current_setting('provisa.user_region')"

Фильтр объединяется через AND с предложением WHERE запроса. Работает как для запросов, так и для мутаций (UPDATE/DELETE). (REQ-035, REQ-041)

Маскирование на уровне столбцов

Маскирование определяется один раз для столбца — это свойство столбца, а не роли. Поле unmasked_to управляет тем, какие роли его обходят. (REQ-249)

Тип маски Поддерживаемые типы SQL-выражение
regex Строка (varchar, char, text) REGEXP_REPLACE(col, pattern, replace)
constant Любой Литеральное значение (NULL, 0, пользовательское)
truncate Дата/Timestamp DATE_TRUNC(precision, col)

Маскирование встраивается в проекцию SQL SELECT — база данных возвращает замаскированные данные. (REQ-263) Немаскированные данные никогда не пересекают провод для замаскированных ролей. (REQ-263) Замаскированные столбцы также блокируются в предложениях WHERE и HAVING (защита предикатов уровня 5), чтобы предотвратить вывод немаскированного значения через фильтрацию. (REQ-263, REQ-531)

Выборка

Все роли видят выборочные результаты (по умолчанию: 100 строк), если у них нет возможности full_results. (REQ-554) Управляется через переменную окружения PROVISA_SAMPLE_SIZE. (REQ-554)

Аудит-логирование

Каждый запрос, затрагивающий актив домена, записывается в неизменяемый (append-only) query_audit_log. (REQ-596, REQ-613) Каждая строка фиксирует tenant_id, user_id, role_id, хэш SHA-256 текста запроса, table_ids, source, status_code, duration_ms и logged_at. (REQ-596) Текст запроса никогда не хранится дословно — только его хэш. (REQ-596)

Журнал неизменяем на уровне базы данных: правила PostgreSQL блокируют DELETE и UPDATE. (REQ-596, REQ-613) Два индекса — (tenant_id, logged_at) и (user_id, logged_at) — поддерживают запросы соответствия по временным диапазонам в рамках арендатора и для отдельного пользователя. (REQ-596, REQ-613)

Когда включено шифрование, столбец хэша текста запроса хранится зашифрованным и расшифровывается только при авторизованных чтениях администратором. (REQ-689)

Ограничение частоты запросов

Ограничения частоты для каждой роли настраиваются в provisa.yaml: максимум запросов в секунду, максимум одновременных подписок SSE и максимум одновременных потоков Arrow Flight. (REQ-369) Ограничения применяются на уровне API до компиляции или выполнения; запросы сверх лимита отклоняются с HTTP 429 и заголовком Retry-After. (REQ-369)

Сервис NL-запросов (POST /query/nl) имеет независимый лимит через nl.rate_limit (запросов в минуту на роль). Запросы сверх лимита отклоняются до какого-либо вызова LLM. (REQ-370)

Состояние ограничения частоты хранится в Redis (cache.redis_url) как счётчик скользящего окна — без состояния для каждого экземпляра — поэтому лимиты действуют на всех горизонтальных экземплярах Provisa. (REQ-371)

Аутентификация

Подключаемые провайдеры аутентификации: (REQ-120)

Провайдер Тип токена Сценарий использования
none Заголовок X-Provisa-Role Разработка
basic Локальные учётные записи bcrypt + JWT Автономные развёртывания
firebase ID-токен Firebase Продакшен
keycloak JWT Keycloak Предприятие
oauth OIDC JWT PingFed, Okta, Azure AD, Auth0
simple bcrypt + JWT Тестирование

Сопоставление ролей: утверждения identity → роль Provisa через настраиваемые правила. (REQ-120) Поле assignments_source управляет тем, откуда поступают назначения ролей: claims считывает их из утверждений JWT-токена (по умолчанию), provisa считывает их из внутреннего хранилища назначений Provisa. (REQ-551)

Суперпользователь, настроенный в provisa.yaml (имя пользователя плюс пароль из секрета окружения), всегда получает роль admin и все возможности независимо от настроенного провайдера — путь для начальной настройки при загрузке. (REQ-125)

Поверхности и учётные данные

Каждая поверхность аутентифицируется через один и тот же контракт провайдера, поэтому учётные данные, работающие на одной, работают на всех, где протокол способен их передать. (REQ-124, REQ-1263) Эта таблица — единственный справочник; документы отдельных поверхностей её не повторяют.

Поверхность Пароль Токен провайдера Персональный токен доступа Клиентский сертификат (mTLS)
HTTP (REST, JSON:API, GraphQL) Authorization: Basic Authorization: Bearer Authorization: Bearer через терминирующий прокси
pgwire поле пароля (открытый текст или SCRAM) поле пароля, развёртывания OIDC поле пароля да
Bolt схема basic схема bearer схема bearer да
Arrow Flight token в рукопожатии или в полезной нагрузке тикета то же да
gRPC метаданные authorization метаданные authorization да
MCP Authorization: Bearer Authorization: Bearer через терминирующий прокси

Там, где в ячейке стоит , протокол не несёт поля имени пользователя, с которым можно было бы связать пароль; эти случаи покрывают токенные формы. pgwire — зеркальный случай: в стартовом пакете одно поле секрета и никакой схемы, поэтому метод выбирается по тому, чем является секрет — PAT распознаётся по префиксу, секрет читается как bearer-токен, когда настроенный провайдер является токен-провайдером, а всё остальное считается паролем. Выбор делается один раз — учётные данные, отвергнутые выбранным валидатором, не проверяются повторно другим.

Матрица обеспечивается тестом tests/unit/test_auth_surface_conformance.py, который вызывает настоящую точку входа валидации каждой поверхности и падает, когда новая поверхность добавлена без строки.

Персональные токены доступа

PAT — это долгоживущий bearer-секрет, который пользователь создаёт для клиента, не способного пройти интерактивный вход: скрипта, BI-инструмента, драйвера. (REQ-1263) Он несёт собственную организацию и роль, и каждая поверхность разрешает его через один и тот же валидатор, поэтому ни одной поверхности не нужно знать, что такое PAT.

Форма на проводе — provisa_pat_ и далее 43 URL-безопасных символа base64. Именно префикс направляет предъявленный секрет в хранилище токенов, а не к провайдеру идентификации, и делает утёкший токен находимым через grep в журналах и репозиториях.

  • Хранение — сохраняется только SHA-256 секрета. Сам секрет показывается ровно один раз, при создании, и не может быть восстановлен. В списке присутствуют отображаемый префикс и отметки времени жизненного цикла, но никогда — рабочие учётные данные.
  • Выпуск и отзывPOST /auth/tokens, GET /auth/tokens, DELETE /auth/tokens/{token_hash}, а также раздел самообслуживания в собственном профиле пользователя в административном интерфейсе. Создание и отзыв учётных данных — действие их владельца.
  • Атрибуция — валидный PAT разрешается в учётную запись владельца: идентификатор пользователя, адрес электронной почты и отображаемое имя. Поэтому строка аудита или отчёт об использовании, записанные под PAT, называют человека, а не учётные данные. Какой именно из токенов этого человека действовал, передаётся отдельно, в raw_claims["token_name"].
  • Срок действия — токен может нести срок действия; истёкший токен отклоняется при валидации. Удаление членства пользователя отзывает вместе с ним его токены.

SCRAM-SHA-256 в pgwire

При провайдере basic установка auth.scram: true заставляет pgwire объявлять SASL (код аутентификации 10) с механизмом SCRAM-SHA-256, так что пароль доказывается, а не отправляется. (REQ-1394) Привязка канала (SCRAM-SHA-256-PLUS) не предлагается.

SCRAM нужен верификатор по RFC 5802, который нельзя вывести из хеша bcrypt. Верификатор записывается всякий раз, когда пароль проходит в открытом виде — регистрация, вход, смена пароля, сброс администратором, — поэтому развёртывание, включившее SCRAM, накапливает верификаторы по мере следующей аутентификации своих пользователей, и первое SCRAM-подключение каждого пользователя следует за его следующим вводом пароля. Пользователю, у которого верификатора ещё нет, отвечают имитацией обмена, неотличимой от настоящей, так что провод не выдаёт, кто уже мигрировал.

Взаимный TLS

Проверка клиентского сертификата переносит первую проверку в TLS-рукопожатие: вызывающий без сертификата, подписанного центром сертификации развёртывания, никогда не доходит до слоя учётных данных. (REQ-1228) Она доступна в pgwire, Bolt, gRPC и Arrow Flight — четырёх транспортах, которые сами терминируют свой TLS.

Переменная Значение
PROVISA_MTLS_CLIENT_CA PEM-набор центра(ов) сертификации, которым разрешено подписывать клиентские сертификаты
PROVISA_MTLS_MODE required (значение по умолчанию, как только задан CA) или optional
PROVISA_MTLS_BIND_PRINCIPAL Когда истинно, common name сертификата должен совпадать с именем пользователя, под которым затем аутентифицируется соединение

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

Ограничение попыток входа

Подбор паролей не зависит от протокола: одну и ту же учётную запись можно долбить по HTTP, pgwire и Bolt. Поэтому счётчик находится на уровне валидации учётных данных, а не на какой-то одной поверхности, так что блокировка, заработанная где угодно, применяется везде. (REQ-1393)

Оно включено по умолчанию — пять неудач за пять минут блокируют субъект на пятнадцать минут — и настраивается в auth.login_throttle. Заблокированному субъекту отказывают ещё до того, как учётные данные вообще будут проверены, а успешная аутентификация очищает историю этого субъекта.

Ключом служит principal, который несёт протокол. Поверхность, работающая только с bearer, principal не несёт, поэтому ключом становится дайджест самих учётных данных; это останавливает неограниченное воспроизведение одного скомпрометированного токена. Хранилище — на процесс, поэтому развёртывание с несколькими API-воркерами допускает до max_attempts на воркер: ограничение — тормоз для подбора, а не распределённая квота.

Адресация организации в проводном протоколе

При мультиарендности организация адресуется по имени хоста: acme.provisa.dev — это организация acme. По HTTP это имя приходит в заголовке Host. Клиент pgwire или Bolt такого заголовка не отправляет, но отправляет имя хоста, к которому подключался, в TLS ClientHello, и Provisa читает организацию оттуда. (REQ-1234) На стороне клиента ничего не меняется — достаточно подключиться к acme.provisa.dev.

Имя хоста — это запрос, а не разрешение. Оно попадает в тот же резолвер, что и заголовок Host, который отклоняет любую организацию, в которой аутентифицированный principal не состоит и на которую не имеет межорганизационного права. Подключение к имени хоста, в котором у вас нет членства, не достигает никаких данных. Клиент, подключившийся по IP-адресу, имени хоста не отправляет и разрешает свою организацию только из principal — так происходит с каждым соединением в развёртывании с одной организацией.

gRPC, Arrow Flight и MCP передают свои сертификаты библиотекам, не предоставляющим обратного вызова по имени хоста; эти транспорты называют организацию заголовком метаданных x-provisa-org.

Режим высокой безопасности

security.mode: high в provisa.yaml утверждает одну гарантию: бэкенд Provisa никогда не работает с данными в открытом виде. (REQ-693) Каждый значимый столбец зашифрован в источнике, и прочитать его может только клиент, владеющий ключом расшифровки. У этой гарантии есть последствия, которые развёртывание должно учесть.

Что делает режим:

  • Конечные точки данных требуют доказательства расшифровки на стороне клиента. Всё под /data/ возвращает 403, если вызывающий не предъявляет заголовок X-Provisa-KMS-Key — признак клиента JDBC или Python, настроенного расшифровывать локально. Браузер или REST-потребитель, работающий с открытым текстом, такого ключа не несёт и получает отказ. Это запрет по умолчанию на всё дерево: маршрут, добавленный завтра, закрыт в день своего выпуска, а исключение требуется обосновать.
  • Конечные точки метаданных схемы остаются открытыми. /data/sdl, /data/introspection, /data/schema-version, /data/domains, /data/proto и /data/compile не возвращают данные строк, а клиент обязан прочитать схему — включая то, какие поля помечены @encrypted, — прежде чем вообще сможет подключиться.
  • gRPC и Arrow Flight продолжают обслуживать при том же доказательстве. Это транспорты, которыми шифрующие клиенты действительно пользуются; их закрытие оставило бы развёртывание высокой безопасности без проводного протокола. Вызов данных на любом из них должен нести тот же ключ KMS в метаданных вызова.
  • pgwire, Bolt и MCP не запускаются. Ни у одного из трёх нет рукопожатия на соединение, способного нести контекст расшифровки: набор строк pgwire и результат Cypher идут по проводу открытым текстом, а вызов инструмента MCP передаёт результаты модели как текст. Настроенный порт для любого из них при старте отклоняется, а не обслуживается.
  • Защиту связей обойти нельзя. И ignore_relationships, и relationship_guard: false игнорируются; см. Управление связями.

Как убедиться, что развёртывание в этом режиме: журнал запуска называет его, запрос /data/sql без ключа KMS отвечает 403 с сообщением, называющим REQ-693, а порты pgwire, Bolt и MCP не слушают.

Хук утверждения ABAC

Опциональный внешний хук политики, срабатывающий перед выполнением запроса. (REQ-203) При настройке Provisa обращается к вашему движку политик с identity пользователя, ролями, таблицами, столбцами и операцией. Ответ определяет, продолжится ли запрос. (REQ-203)

Область действия

Хук срабатывает только когда запрос затрагивает таблицу или источник в области действия — нулевые накладные расходы для всего остального. (REQ-204)

Конфигурация Эффект
auth.approval_hook.scope: all Каждый запрос запускает хук
sources[].approval_hook: true Все таблицы этого источника запускают хук
tables[].approval_hook: true Эта таблица запускает хук

Протоколы

Поддерживаются три транспорта: (REQ-246)

Тип Сценарий использования Поле конфигурации
webhook Любой HTTP-совместимый сервис политик (OPA, пользовательский) url
unix_socket OPA или сайдкар политик на той же машине socket_path + url
grpc Высокопроизводительный совместно размещённый сервис политик url (host:port)

Транспорт gRPC использует контракт provisa.auth.ApprovalService, определённый в provisa/auth/approval.proto. Реализуйте этот сервис в вашем движке политик: (REQ-246)

service ApprovalService {
  rpc Evaluate (ApprovalRequest) returns (ApprovalResponse);
}

message ApprovalRequest {
  string user = 1;
  repeated string roles = 2;
  repeated string tables = 3;
  repeated string columns = 4;
  string operation = 5;
}

message ApprovalResponse {
  bool approved = 1;
  string reason = 2;
}

Канал gRPC постоянен — один канал на экземпляр Provisa, повторно используемый для всех вызовов к этому эндпоинту хука. (REQ-555)

Запрос / Ответ

Все три транспорта несут одну и ту же полезную нагрузку: (REQ-246)

Поле Тип Описание
user string Identity аутентифицированного пользователя
roles string[] Роли Provisa пользователя
tables string[] Идентификаторы таблиц, на которые ссылается запрос
columns string[] Столбцы, выбранные в запросе
operation string "query" или "mutation"

Транспорты webhook и Unix socket обмениваются JSON. Ответ должен включать approved (bool) и опционально reason (string). (REQ-246)

Таймаут и запасной вариант

auth:
  approval_hook:
    type: grpc          # webhook | grpc | unix_socket
    url: "localhost:50051"
    timeout_ms: 500     # default 5000
    fallback: deny      # allow | deny — applied on timeout or error
    scope: ""           # "" = use per-table/per-source flags; "all" = every query

При таймауте или ошибке транспорта применяется политика fallback. (REQ-247) Автоматический выключатель (circuit breaker) (по умолчанию: открывается после 5 последовательных сбоев, полуоткрывается через 30 с) предотвращает каскадные сбои от медленного эндпоинта хука. (REQ-556)

Пример конфигурации

auth:
  approval_hook:
    type: webhook
    url: "http://opa.internal:8181/v1/data/provisa/allow"
    timeout_ms: 300
    fallback: deny

sources:
  - id: analytics_pg
    approval_hook: true   # all tables on this source require hook approval

tables:
  - id: salary_data
    approval_hook: true   # this table always requires hook approval

Секреты

Учётные данные используют синтаксис ${env:VAR_NAME}, разрешаемый во время выполнения. (REQ-557) Пароли никогда не хранятся в БД конфигурации. (REQ-557)