Глава 27. Где именно принимается решение
27.1. Один запрос — несколько уровней
Наивная схема выглядит так:
Request
↓
ALLOW / DENY
Но в реальной системе существуют как минимум два разных решения.
Первое:
Можно ли работать с объектом?
Второе:
Какие физические данные должен вернуть запрос?
Поэтому:
Object authorization
и:
Data enforcement
не следует смешивать.
27.2. Три разные ответственности
Удобно различать:
BUILD
DECIDE
ENFORCE
Build — построить производные security facts.
Decide — определить, разрешено ли действие над объектом.
Enforce — физически ограничить данные.
Схема:
Source facts
↓
BUILD
↓
Security projections
↓
DECIDE
↓
Object authorization
↓
ENFORCE
↓
RLS / physical data
27.3. Где находится логическое решение
Логическое решение имеет форму:
Can(Subject, Action, Object)?
Оно может приниматься приложением, базой данных или специализированным механизмом.
Важно не место само по себе.
Важно, чтобы решение использовало определённую модель и чтобы последующие уровни не могли случайно расширить доступ.
27.4. Где находится физическое ограничение
Физическое ограничение действует ближе к данным.
Например:
Application
↓
SQL
↓
PostgreSQL RLS
↓
Rows
Здесь база данных уже не решает весь вопрос:
Почему этот пользователь имеет отношение к этому объекту?
Она применяет заданную модель к физическим строкам.
27.5. Два уровня Guardian
В Guardian это можно представить так:
Security projections
↓
has_object_permission()
↓
RlsDataSourceProxy
↓
PostgreSQL RLS
↓
Physical rows
Первый уровень работает с логическим объектом.
Второй — с физическими данными.
27.6. Концептуальный псевдокод
В общем виде:
function access(request):
authorization = authorizeObject(request)
if authorization != ALLOW:
return DENY
return queryData(request)
Далее:
queryData(request)
↓
database
↓
RLS
↓
allowed rows
Это не означает, что каждый конкретный сервис обязан реализовывать именно такой вызов.
Это разделение архитектурных обязанностей.
27.7. Почему нельзя считать RLS второстепенным
Если приложение разрешило:
READ Object X
но SQL способен получить строки, относящиеся к:
Object Y
то объектная авторизация сама по себе недостаточна.
RLS может выступить последней защитной границей.
Это особенно важно для:
-
массовых запросов;
-
сложных репозиториев;
-
отчётов;
-
фоновых операций;
-
новых API, которые появились позже основной логики авторизации.
27.8. Но и RLS не должен быть единственным механизмом
Обратная ошибка — перенести всю бизнес-авторизацию в RLS.
Тогда SQL policy начинает знать:
roles
groups
projects
publications
delegations
lifecycle
и другие понятия предметной области.
Это превращает физический слой данных в полный authorization engine.
Поэтому границы должны оставаться явными:
Authorization model
↓
Object decision
↓
Data enforcement
27.9. Место решения и источник истины
Нужно различать три понятия:
Source of truth
Decision point
Enforcement point
Например:
Source facts
↓
Security projections
↓
Object decision
↓
RLS enforcement
Проекция не является источником истины.
Точка принятия решения не обязана быть местом хранения фактов.
RLS не является источником модели доступа.
27.10. Главный вывод
У сложной системы доступа нет необходимости иметь одну магическую функцию:
checkAccess()
Вместо этого существует цепочка ответственности:
BUILD
↓
prepared security state
DECIDE
↓
object authorization
ENFORCE
↓
physical data boundary
Это позволяет разделить сложность модели и сложность её применения к данным.