одинаково применять правило ко всем случаям
Пример: простое правило доступа, которое становится двусмысленным на масштабе
Что теряется при переходе: причины отказа остаются неявными в булевых результатах
Что достигается: одинаковая обработка одинаковых случаев
Правило как формула: доступ = ((owner ИЛИ auditor) И (recheck И НЕ blocked))
Факты:
учётная запись заблокирована=0тот же tenant=1владелец=1аудитор=0повторная проверка=1
Значение формулы: 1
разрешено (версия политики access-policy/1)
Голое решение: 1. Решение с причиной: {:allowed, "access-policy/1"}.
Из первого причина отказа не восстанавливается — в этом бедность
boolean как результата.
Таблица решений — сработала строка №4
| № |
заблокирован |
тот же tenant |
владелец/аудитор |
решение |
| 1 |
1 |
любое |
любое |
deny: account-blocked |
| 2 |
0 |
0 |
любое |
deny: tenant-mismatch |
| 3 |
0 |
1 |
0 |
deny: missing-grant |
| 4 |
0 |
1 |
1 |
allow (access-policy/1) |
первое совпадение выигрывает, дальше строки не смотрим. локальные проверки идут раньше удалённого сервиса ролей: иначе система тратит сеть на заведомо отказные случаи и даёт утечку через разницу во времени ответа
Расхождение таблицы и формулы: таблица решений не знает про «повторную проверку», которая есть в формуле: расхождений 3, все при recheck=0 — формула отказывает, таблица разрешает.
Таблица истинности формулы доступа
| auditor | blocked | owner | recheck |
доступ |
| 1 | 1 | 1 | 1 |
0 |
| 1 | 1 | 1 | 0 |
0 |
| 1 | 1 | 0 | 1 |
0 |
| 1 | 1 | 0 | 0 |
0 |
| 1 | 0 | 1 | 1 |
1 |
| 1 | 0 | 1 | 0 |
0 |
| 1 | 0 | 0 | 1 |
1 |
| 1 | 0 | 0 | 0 |
0 |
| 0 | 1 | 1 | 1 |
0 |
| 0 | 1 | 1 | 0 |
0 |
| 0 | 1 | 0 | 1 |
0 |
| 0 | 1 | 0 | 0 |
0 |
| 0 | 0 | 1 | 1 |
1 |
| 0 | 0 | 1 | 0 |
0 |
| 0 | 0 | 0 | 1 |
0 |
| 0 | 0 | 0 | 0 |
0 |
Де Морган как инструмент ревью
исходное: НЕ (owner ИЛИ auditor)
правильно: (НЕ owner И НЕ auditor)
типовая ошибка: (НЕ owner ИЛИ НЕ auditor) — расходится при %{owner: false, auditor: true}
Восемь групп законов — проверены перебором
коммутативность: 8 наборов, нарушений 0
ассоциативность: 16 наборов, нарушений 0
дистрибутивность: 16 наборов, нарушений 0
тождества: 8 наборов, нарушений 0
дополнения: 6 наборов, нарушений 0
идемпотентность: 4 наборов, нарушений 0
де Морган: 8 наборов, нарушений 0
поглощение: 8 наборов, нарушений 0
Ход разбора — 6 шагов
-
Правило стало формулой: доступ = ((owner ИЛИ auditor) И (recheck И НЕ blocked)).
формулаоперациябулева_переменная
Спор о правиле теперь решается вычислением, а не пересказом.
-
Набор значений: blocked=0, same_tenant=1, owner=1, auditor=0, recheck=1.
набор_значенийзначениебулева_переменная
Каждое условие — да или нет; ничего третьего на этой ступени нет.
-
Значение формулы на этом наборе: 1 (доступ по формуле есть).
формулазначениетаблица_истинности
Одинаковые наборы обязаны дать одинаковый ответ — в этом вся польза.
-
Сработала строка №4 таблицы решений: разрешить (версия политики access-policy/1).
таблица_решенийпорядок_правилкороткое_замыкание
Первое совпадение выигрывает; порядок строк — архитектурное решение.
-
Голое решение: 1. Решение с причиной: разрешено, версия access-policy/1.
причина_отказаверсия_политикитаблица_решений
«Нельзя» без причины — это ответ, из которого нельзя понять, что исправить: решение должно нести основание.
-
Отрицание правила: «НЕ (owner ИЛИ auditor)» — это «(НЕ owner И НЕ auditor)», а не «(НЕ owner ИЛИ НЕ auditor)». Вторая запись меняет правило, расходясь на наборе %{owner: false, auditor: true}.
законравносильностьотрицаниеревью
Отрицание вносится внутрь по закону, а не на глаз.
Проверка на анахронизмы:
:ok
На этом уровне нет предикатов, кванторов и отношений между объектами:
переменная тут булева, а не обозначающая человека или отчёт.