Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Глава 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

Это позволяет разделить сложность модели и сложность её применения к данным.