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

Глава 15. Исходные факты и производные факты

15.1 Не всё, что используется для доступа, произошло непосредственно

В предыдущей главе мы рассмотрели цепочки отношений.

Например:

Alice
  ↓ member
Group A
  ↓ belongs to
Project P
  ↓ contains
Document A

Из этой структуры можно получить вывод:

Alice → READ → Document A

Но сам этот вывод не является исходным фактом.

В системе действительно существуют:

Alice is member of Group A
Group A belongs to Project P
Project P contains Document A

А утверждение:

Alice can READ Document A

уже является результатом применения модели к этим фактам.

Это различие становится фундаментальным для архитектуры.


15.2 Исходный факт описывает состояние системы

Исходный факт — это утверждение, которое непосредственно фиксирует состояние или отношение в предметной модели.

Например:

Alice → member of → Group A

или:

Document A → belongs to → Project P

или:

Alice → owner of → Document A

Такие факты могут быть созданы, изменены или удалены в результате операций системы.

Они являются исходными данными для дальнейших рассуждений.


15.3 Производный факт получается из других фактов

Теперь рассмотрим:

Alice → READ → Document A

Если такого отношения непосредственно не существует, но оно следует из нескольких других фактов и правил, это производный факт.

Например:

Alice → member → Group A
Group A → access → Project P
Document A → belongs → Project P

и правило:

member + access + belongs
        ↓
       READ

Тогда:

Alice → READ → Document A

получается вычислением.

Его значение не в том, что он был непосредственно записан пользователем.

Его значение в том, что он следует из модели.


15.4 Факт и вывод находятся на разных уровнях

Можно представить два уровня:

ИСХОДНЫЕ ФАКТЫ

Alice → member → Group A
Group A → access → Project P
Document A → belongs → Project P


ПРАВИЛО

member + access + belongs → READ


ПРОИЗВОДНЫЙ ФАКТ

Alice → READ → Document A

Это три разных элемента.

Не следует смешивать:

факт

и:

правило

а также:

правило

и:

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

15.5 Почему это различие важно

Если всё хранить как один набор отношений, становится непонятно:

это произошло в предметной области или было вычислено системой безопасности?

Например:

Alice → member → Group A

может быть реальным членством.

А:

Alice → READ → Document A

может быть результатом этого членства.

Если эти два утверждения имеют одинаковый статус, становится трудно понять:

  • откуда взялось разрешение;

  • почему оно исчезло;

  • какой факт нужно изменить;

  • можно ли изменить его непосредственно;

  • что произойдёт после изменения исходного отношения.

Поэтому происхождение факта является частью архитектурной модели.


15.6 Производный факт не становится исходным оттого, что его сохранили

Это особенно важное различие.

Предположим, система вычислила:

Alice → READ → Document A

и сохранила этот результат в отдельной таблице.

Физически он теперь существует в базе данных.

Но семантически он всё ещё остаётся производным.

Его источник:

Alice → member → Group A
Group A → access → Project P
...

не исчезает.

Поэтому:

физическое хранение результата не превращает результат в исходный факт.

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


15.7 Производный факт имеет источник

Если существует:

Alice → READ → Document A

полезно понимать:

почему?

Например:

Причина:
Alice member of Group A
Group A has access to Project P
Document A belongs to Project P

Или:

Причина:
Alice is owner of Document A

Производный факт поэтому можно рассматривать как результат функции:

DerivedFact = F(SourceFacts, Rules)

Если исходные факты или правила изменились, результат потенциально тоже должен измениться.


15.8 Производный факт может исчезнуть без изменения объекта

Рассмотрим:

Document A

Сам объект не менялся.

Но Alice покинула группу:

Alice → member → Group A

больше не существует.

Если именно это членство было частью основания доступа, результат:

Alice → READ → Document A

становится недействительным.

Таким образом:

Object unchanged
Source relation changed
        ↓
Derived permission changed

Это важный архитектурный эффект.

Доступ может измениться без изменения самого защищаемого объекта.


15.9 Производный факт может измениться без изменения исходного объекта

Та же ситуация может возникнуть при изменении другого отношения.

Например:

Document A → published in → Project P

была отозвана публикация.

Сам документ остался тем же.

Но если публикация была необходима для доступа, производный результат меняется:

READ → no longer applicable

Получается:

Изменение отношения
        ↓
Изменение контекста
        ↓
Изменение производного результата

И снова сам объект может оставаться неизменным.


15.10 Один исходный факт может влиять на множество результатов

Предположим:

Alice → member → Group A

Group A имеет доступ к большому числу объектов.

Тогда изменение одного членства может повлиять сразу на:

Document A
Document B
Document C
...

То есть:

1 source fact
      ↓
many derived facts

Это уже не локальное изменение одного объекта.

Изменение находится в одном месте модели, а последствия распространяются по связанным отношениям.


15.11 Один производный факт может зависеть от нескольких источников

Обратная ситуация:

Alice → member → Group A
Group A → access → Project P
Document A → belongs → Project P

Результат:

Alice → READ → Document A

зависит сразу от нескольких фактов.

Если удалить любой необходимый элемент цепочки, результат может исчезнуть.

Поэтому производный факт имеет не только значение, но и зависимости.


15.12 Зависимости образуют отдельную структуру

Можно представить:

Alice ──member──> Group A
                    │
                  access
                    ↓
                 Project P
                    ↑
                 belongs
                    │
                Document A

и вывод:

Alice → READ → Document A

Зависит от этой структуры.

Если изменить:

Alice → member → Group A

необходимо определить все производные результаты, которые от него зависят.

Если изменить:

Document A → belongs → Project P

картина будет другой.

Следовательно, изменение исходного факта имеет область последствий.


15.13 Производные факты не должны становиться вторым источником истины

Предположим, система хранит:

Source:
Alice → member → Group A

Derived:
Alice → READ → Document A

Если затем удалить членство, но оставить производный факт, система окажется в противоречивом состоянии.

Получится:

Source says: no membership
Derived says: READ

Возникает вопрос:

чему верить?

Поэтому необходимо определить источник истины.

Производный результат не должен независимо утверждать состояние, противоречащее его источникам.


15.14 Производный факт можно пересчитать

Если производный факт является функцией исходных фактов и правил:

D = F(S, R)

то при необходимости его можно получить заново:

S + R
 ↓
F
 ↓
D

Это важное свойство.

Если производное состояние повреждено, его в принципе можно восстановить из источников.

Именно поэтому разделение исходных и производных данных имеет не только логический, но и практический смысл.


15.15 Но пересчитывать всё не всегда разумно

Пусть система содержит:

10 000 000 users
1 000 000 objects
100 000 000 relations

Изменился один факт:

Alice → member → Group A

Теоретически можно заново вычислить всю модель.

Но это означает обработку огромного количества данных, большая часть которых не связана с изменением.

Гораздо разумнее определить:

что изменилось
        ↓
от чего это зависит
        ↓
какие результаты затронуты

и пересчитать только их.

Пока это ещё не вопрос конкретной технологии.

Это следствие самой структуры зависимости.


15.16 Изменение факта и изменение результата — разные события

Пусть произошло:

Alice left Group A

Это изменение исходного состояния.

А затем:

Alice lost READ on Document A
Alice lost UPDATE on Document B
Alice lost APPROVE on Document C

Это уже последствия изменения.

Важно различать:

Source Change

и:

Derived Changes

Потому что они имеют разную семантику.

Первое изменяет модель предметной области.

Второе изменяет её вычисленное представление.


15.17 Не каждый производный факт относится к безопасности

Производные данные встречаются далеко не только в авторизации.

Например:

Order total

может быть вычислен из строк заказа.

Account balance

может быть вычислен из операций.

Search index

может быть построен из документов.

Effective permission

может быть вычислен из отношений доступа.

Во всех случаях действует одна и та же идея:

Исходное состояние
      ↓
Правила вычисления
      ↓
Производное состояние

Поэтому разделение source и derived — общий архитектурный принцип, а не специальный механизм безопасности.


15.18 В безопасности производные факты особенно чувствительны

В обычном вычислении ошибка в производном значении может привести к неправильному отображению данных.

В модели доступа ошибка может привести к другому результату:

Derived = ALLOW

когда исходные факты уже не дают основания для доступа.

Или наоборот:

Derived = DENY

когда основание существует.

Поэтому согласованность производного состояния с исходными фактами становится частью безопасности системы.


15.19 Контекст строится из исходных фактов

Вернёмся к определению контекста.

В главе 3 мы определили его как совокупность фактов и отношений, значимых для конкретного решения.

Теперь можно уточнить происхождение этих фактов.

Контекст может быть построен из:

исходных фактов
+
релевантных отношений
+
производных фактов

Но они не имеют одинакового статуса.

Исходные факты являются основанием модели.

Производные факты являются результатом её применения.


15.20 Производный факт может стать входом следующего вычисления

Это ещё один важный случай.

Например:

Alice → member → Group A

из этого можно получить:

Alice → member of Project P

а затем:

Alice → READ → Document A

Получается цепочка вычислений:

Source Facts
     ↓
Derived Fact 1
     ↓
Derived Fact 2
     ↓
Access Result

Но это не означает, что все уровни становятся исходными.

Каждый уровень имеет собственный источник.


15.21 Поэтому полезно различать слои фактов

Условно модель можно представить так:

┌─────────────────────────────┐
│       Исходные факты        │
│ отношения, состояния, роли  │
└──────────────┬──────────────┘
               ↓
┌─────────────────────────────┐
│     Производные факты       │
│  результаты правил модели   │
└──────────────┬──────────────┘
               ↓
┌─────────────────────────────┐
│    Эффективные разрешения   │
└──────────────┬──────────────┘
               ↓
┌─────────────────────────────┐
│     Решение по запросу      │
└─────────────────────────────┘

Границы между этими слоями помогают понимать, что именно нужно изменять и что необходимо пересчитывать.


15.22 Решение о доступе тоже не обязательно является фактом

Есть ещё одно различие.

Пусть система получила:

Effective Permission = READ

Это производный факт.

Но конкретный запрос:

Alice requests READ on Document A

даёт решение:

ALLOW

Решение зависит от конкретного действия.

Поэтому:

Effective Permission ≠ Access Decision

Даже если эффективное разрешение уже вычислено, конкретный запрос ещё должен быть сопоставлен с ним.


15.23 Производный факт должен иметь определённую семантику

Если в системе появляется таблица или представление:

Alice → READ → Document A

необходимо понимать, что именно означает эта запись.

Она может означать:

  • Alice имеет право читать объект;

  • Alice потенциально может читать объект;

  • Alice прошла последнюю проверку;

  • Alice была связана с объектом через определённый путь;

  • Alice получила разрешение из конкретной роли.

Это разные утверждения.

Поэтому название и семантика производного факта должны соответствовать тому, что действительно было вычислено.


15.24 Нельзя подменять исходную модель её проекцией

Представим, что исходные факты:

Alice → member → Group A
Group A → access → Project P

были преобразованы в:

Alice → READ → Document A
Alice → READ → Document B
Alice → READ → Document C

Если смотреть только на последние записи, мы видим результат, но не видим причины.

Это опасно не потому, что производная модель плоха.

Наоборот, она может быть очень полезна.

Проблема возникает, если забыть, что это представление исходной модели, а не сама модель.


15.25 Производные данные могут быть оптимизацией

Иногда один и тот же вывод приходится получать очень часто.

Тогда имеет смысл сохранить результат:

F(Source Facts)
        ↓
Materialized Result

вместо повторного вычисления:

Source Facts
        ↓
F
        ↓
Result

при каждом запросе.

Так производный слой становится способом оптимизации.

Но он остаётся корректным только при условии, что его состояние соответствует источнику.


15.26 Чем быстрее меняются источники, тем важнее согласованность

Предположим, отношения доступа меняются постоянно.

Тогда возникает проблема:

Source state:
new

Derived state:
old

В этот момент система располагает двумя версиями реальности.

Для обычной аналитики это может быть приемлемо.

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

Это уже инженерная проблема.

Но она появляется непосредственно из разделения:

source

и:

derived

15.27 Изменение модели должно распространяться дальше

Если изменился исходный факт:

Alice → member → Group A

недостаточно просто записать новое значение.

Необходимо обеспечить, чтобы связанные производные факты больше не противоречили новому состоянию.

Концептуально:

Source changed
      ↓
Affected derived facts identified
      ↓
Derived state updated
      ↓
Model becomes consistent

Способ реализации здесь пока не важен.

Важно само требование.


15.28 Без этого эффективное разрешение становится устаревшим

Предположим:

10:00
Alice is member of Group A
→ READ granted

10:05
Alice leaves Group A

10:06
Derived state still says:
Alice → READ → Document A

На уровне исходной модели доступ уже должен быть другим.

Но производное состояние ещё этого не отражает.

Получается:

Source Model ≠ Security Model

Именно здесь появляется понятие согласованности модели безопасности.

Оно станет центральным в следующих главах.


15.29 Исходные факты и производные факты нельзя смешивать в одну ответственность

Для исходного факта вопрос:

что произошло в системе?

Для производного:

что следует из того, что произошло?

Для эффективного разрешения:

какие действия допустимы в текущей модели?

Для решения:

разрешён ли этот конкретный запрос?

Это разные вопросы.

И если они смешаны в одном уровне системы, становится трудно определить:

  • кто изменяет данные;

  • что нужно пересчитать;

  • где искать причину результата;

  • как восстанавливать состояние;

  • что считать источником истины.


15.30 Что это меняет в архитектуре

До этого момента можно было рассматривать модель доступа как набор отношений и правил.

Теперь появляется временная и причинная структура:

Исходный факт
      ↓
изменение
      ↓
зависимые производные факты
      ↓
эффективные разрешения
      ↓
решение о доступе

Архитектура должна учитывать не только:

что хранить?

но и:

что является источником?
что является результатом?
что от чего зависит?
что нужно изменить после изменения источника?

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


15.31 Главный вывод

В модели доступа необходимо различать исходные факты и производные факты.

Исходный факт непосредственно описывает состояние системы:

Alice → member → Group A

Производный факт следует из исходных фактов и правил:

Alice → READ → Document A

Производный факт может быть сохранён физически, но от этого он не становится источником истины.

Его существование определяется зависимостями:

Source Facts
     +
Rules
     ↓
Derived Facts

Поэтому изменение доступа может начинаться далеко от защищаемого объекта.

Изменение одного отношения способно изменить множество разрешений, а одно разрешение может зависеть сразу от нескольких исходных фактов.

Именно здесь появляется следующая архитектурная проблема:

если производные факты нужно поддерживать в соответствии с исходными, их недостаточно просто вычислить один раз — необходимо заранее определить, как модель будет восстанавливаться и изменяться при каждом изменении исходного факта.

Этому посвящена следующая глава.