Documentation
¶
Index ¶
- Constants
- Variables
- func CanonicalModelRelPath() string
- func IsVerb(rel string) bool
- func ResolveCanonicalModel() (path string, dsl []byte, err error)
- func ResolveCanonicalModelFrom(root string) (path string, dsl []byte, err error)
- func ShippedModelRelPath() string
- type Atom
- type AtomKind
- type Census
- type DirectSubject
- type Model
- func (m *Model) AssertOnePointerPerParentType() error
- func (m *Model) Census() Census
- func (m *Model) Compile(typeName, relation string) (Plan, error)
- func (m *Model) IsPointer(typeName, rel string) bool
- func (m *Model) PointerTargets(ptr *Relation) []string
- func (m *Model) Type(name string) *ModelType
- func (m *Model) TypeNames() []string
- type ModelType
- type Plan
- type Relation
- type Term
- type TermKind
Constants ¶
const MaxPointerDepth = 4
MaxPointerDepth — предел глубины цепи указателей на предка.
Экспортирован намеренно: предел — часть КОНТРАКТА компиляции, а не деталь. Он объявляет, сколько уровней иерархии областей вообще может быть пройдено, и потребитель (в том числе прибор замера) обязан видеть ту же величину, иначе два места разойдутся в том, что считают «слишком глубоко».
const VerbPrefix = "v_"
VerbPrefix — приставка отношения-глагола.
Она не выбрана здесь, а выведена из продукта: имя отношения-глагола собирает реконсайлер iam, приводя глагол роли к канонической форме и приписывая эту приставку (комментарий у `v_addtargets` в самой модели это и фиксирует). Поэтому «глагол» здесь — свойство ИМЕНИ, и держит его сама модель, а не память автора.
Приведение имени происходит НЕ ЗДЕСЬ, и назвать его действующей защитой этого пакета было бы неправдой: пакет разбирает уже готовый текст модели и имён не строит. Прежняя редакция называла функцию приведения по имени — после переезда компилятора из прибора в прод она осталась в другом пакете, и комментарий стал утверждать, что сегмент закрыт тем, чего в этом доме нет. Поймано гейтом TestCommentsNamingAGuardHaveItInScope, а не чтением: комментарий уже ответил на вопрос «закрыт ли», поэтому проверять его никто не шёл.
Variables ¶
var ErrCanonAboveTree = errors.New("канон лежит выше дерева")
ErrCanonAboveTree — канона в названном дереве нет, а ВЫШЕ него он лежит. Отдельный признак: он отличает «прежде читалось чужое» от «его нет нигде».
var ErrCanonNotInTree = errors.New("канона в дереве нет")
ErrCanonNotInTree — канона нет ни в дереве, ни выше него.
var ErrTreeNotResolved = errors.New("дерево под резолвом не установлено")
ErrTreeNotResolved — дерево назвать нечем: проверка НЕ ИСПОЛНЯЛАСЬ.
var ErrTypeNotDeclared = errors.New("authzplan: тип не объявлен моделью")
Compile строит план вердикта для одного отношения одного типа.
Обход рекурсивный и завершается по построению: ключ посещения несёт ТИП, ИМЯ отношения и путь, а глубина пути ограничена (`MaxPointerDepth`). Цикл в модели не повесил бы обход, а стал бы находкой — и это лучше, чем незавершимый разбор. ErrTypeNotDeclared — тип, о котором спрашивают, в словаре модели не объявлен.
ПОЧЕМУ ЭТО ОТДЕЛЬНАЯ, СИГНАЛЬНАЯ ОШИБКА. По ней вызывающий принимает РЕШЕНИЕ О ДОСТУПЕ, и решения тут два разных. «Типа нет вовсе» — это факт о модели: объектов такого типа не существует, отношений на них не существует, и честный ответ — ОТКАЗ. «Не смог ответить» (сломанный план, недостижимый источник) — это незнание, и выдавать его за отказ запрещено: незнание, поданное как «нет», прячет поломку ровно там, где она опаснее всего.
Различать их сравнением ТЕКСТА нельзя: первая же правка формулировки увела бы вызывающего в другую ветвь, и увела бы молча.
Наблюдалось на снятии внешнего движка (#880): вопрос про снятый тип-носитель точечных выдач кончался ошибкой вместо отказа, и для вызывающего «нет прав» стало неотличимо от «сломалось».
Functions ¶
func CanonicalModelRelPath ¶
func CanonicalModelRelPath() string
CanonicalModelRelPath — тот же относительный путь для вызывающего, который печатает перепись.
Экспортирован ради ОДНОГО читателя — исполнителя сверки: его перепись обязана называть не только корень обхода, но и файл, по которому она снята, иначе «корень назван» и «канон прочитан оттуда» остаются двумя разными утверждениями. Второй копии постоянной это не заводит: возвращается она же.
func ResolveCanonicalModel ¶
ResolveCanonicalModel — канон ТОГО дерева, в котором идёт прогон.
Дерево не выводится арифметикой пути и не ищется подъёмом за маркером: вопрос ставится ИНДЕКСУ (`rev-parse --show-toplevel`), а затем проверяется предпосылка — что каталог, из которого спрашивают, этим деревом ОТСЛЕЖИВАЕТСЯ. Без второй проверки модуль, распакованный внутрь чужого репозитория, получил бы корень ЧУЖОГО дерева и вердикт о нём.
func ResolveCanonicalModelFrom ¶
ResolveCanonicalModelFrom — канон дерева root.
Параметр — КОРЕНЬ ДЕРЕВА, а не путь канона, и различие несущее (#1089, §2 п. 2): подменить канон снимком, лежащим рядом со сверщиком, нечем — предъявить такой путь этой подписи невозможно. Указать можно лишь целое дерево, у которого канон лежит по каноническому относительному пути; ровно этим и пользуется инъекция, подавая своё дерево намеренно.
Читает РОВНО `<root>/<fgaModelRelPath>`. За пределы root не выходит ни при каком входе: файл выше дерева попадает только в ТЕКСТ отказа и никогда в возвращаемые байты.
func ShippedModelRelPath ¶
func ShippedModelRelPath() string
ShippedModelRelPath — ВТОРАЯ постоянная координата: копия канона, которую модуль везёт с собой.
Экспортируется ради одного вопроса, который без неё не задать: КАКОЙ из двух координат прочитан канон. Ответ различает посадки — в дереве платформы читается контракт, в самостоятельной поставке модуля его нет вовсе и читается копия, — и потому отличает «условие сверки не создано» от находки о продукте (kacho#2241). Постоянная возвращается та же; второй копии это не заводит.
Types ¶
type Atom ¶
type Atom struct {
Kind AtomKind
// ParentType — тип предка, на котором лежит факт; пустая строка означает сам
// объект. Путь выражен ТИПОМ, а не последовательностью указателей: в этой
// модели у типа не бывает двух указателей на один и тот же тип предка, и это
// проверяется (`assertOnePointerPerParentType`), а не предполагается.
ParentType string
// Relation — имя отношения. У AtomFact это имя факта, у AtomBinding — ГЛАГОЛ,
// который обязана давать роль привязки. Он не равен спрошенному отношению
// всегда: `v_addtargets` выводится в модели от `v_update`, и привязку надо
// спрашивать про `v_update`. Подставить сюда спрошенное имя значило бы молча
// отказывать держателю права, которое модель ему даёт.
Relation string
Origin string // откуда терм пришёл — для отчёта и для падения пробы
}
Atom — один источник разрешения.
type AtomKind ¶
type AtomKind int
AtomKind — вид источника разрешения в плане формы E.
const ( // AtomBinding — субъект назван в привязке, роль которой даёт спрошенный // глагол, правило-селектор которой накрывает объект, а область которой // содержит объект. Ровно то, что у движка лежит материализованным кортежем. AtomBinding AtomKind = iota // AtomFact — строка факта на объекте или на его предке: владелец аккаунта, // администратор кластера, членство, подстановочная выдача публичного чтения. // Это НЕ материализация состава: строк здесь порядка числа аккаунтов и // привязок, а не числа объектов × глаголов. AtomFact )
type Census ¶
type Census struct {
Types int
Declarations int
VerbTypes int
VerbDeclaration int
Pointers int
Conditions int
}
Census — счётные величины модели. Печатаются в отчёте, потому что «ноль расхождений» обязано быть отличимо от «ноль спрошенного».
type DirectSubject ¶
type DirectSubject struct {
Type string // тип субъекта либо тип-указатель: user, service_account, group, cluster…
Wildcard bool // `user:*`
Userset string // `group#member` → "member"
Condition string // `user with mfa_fresh` → "mfa_fresh"
}
DirectSubject — одна запись прямого списка.
func (DirectSubject) String ¶
func (d DirectSubject) String() string
String возвращает запись в том виде, в каком она стоит в модели.
type Model ¶
type Model struct {
Types []*ModelType
Conditions []string
// pointers — имена отношений-указателей, ВЫВЕДЕННЫЕ из текста: указателем
// считается ровно то отношение, через которое хоть одно объявление ходит
// формой `X from <указатель>`. Признак выводится, а не выписывается: список
// имён в этом файле был бы вторым местом об одном предмете.
// Pointers — тип → имена его указателей на предка. Экспортировано: цепь
// областей есть ЧАСТЬ разобранной модели, и всякий, кто по ней ходит
// (вычисление предков, проверка выразимости, замер), обязан ходить по ЭТОЙ
// карте, а не строить свою из тех же объявлений.
Pointers map[string]map[string]bool
// contains filtered or unexported fields
}
Model — разобранная модель целиком.
func ParseModel ¶
ParseModel разбирает канонический DSL.
Он отвергает вход, а не пропускает непонятое: нераспознанная строка внутри блока `relations`, отношение без термов, пересечение или вычитание (`and` / `but not`) — ошибка. Разбор, молча пропускающий конструкцию, произвёл бы план, который «совпал» с движком потому, что о пропущенном не спросили.
func (*Model) AssertOnePointerPerParentType ¶
assertOnePointerPerParentType проверяет предпосылку, на которой стоит выражение пути ТИПОМ предка вместо последовательности указателей: у типа не должно быть двух указателей, ведущих в один и тот же тип. Предпосылка проверяется, а не предполагается, — иначе первый же такой тип сделал бы два разных источника неразличимыми, и план молча объединил бы их. AssertOnePointerPerParentType — у типа не больше одного указателя на каждый тип-предка.
Экспортирована, потому что это утверждение о МОДЕЛИ, а не шаг компиляции: его обязан уметь задать и тот, кто модель проверяет, не компилируя.
func (*Model) IsPointer ¶
IsPointer отвечает, является ли отношение типа указателем — то есть ходит ли через него хоть одно объявление модели.
func (*Model) PointerTargets ¶
pointerTargets — типы, на которые ведёт отношение-указатель. PointerTargets — типы, на которые ведёт указатель. Экспортировано по той же причине, что и Pointers: это чтение модели, а не шаг компиляции.
type ModelType ¶
type ModelType struct {
Name string
Relations []*Relation
// contains filtered or unexported fields
}
ModelType — один `type`.
type Plan ¶
type Plan struct {
Type string
Relation string
Atoms []Atom
// Conditioned — записи прямых списков, несущие условие на кортеже (`with
// mfa_fresh`). У формы E места под условие НЕТ: её факт — строка, у строки
// нет контекста запроса, и CEL она не исполняет. Поэтому такие записи
// называются поимённо и выносятся в исход (в), а не растворяются в атоме.
Conditioned []string
// Unclassified — термы, которые компилятор НЕ отнёс ни к одному виду.
// Непустой список означает, что план неполон и вердикт по нему давать нельзя.
Unclassified []string
}
Plan — скомпилированный вердикт одного отношения одного типа.
func (Plan) Expressible ¶
Expressible отвечает, выразим ли план целиком.
type Term ¶
type Term struct {
Kind TermKind
Direct []DirectSubject
Computed string
TTURelation string
TTUPointer string
Raw string
}
Term — один дизъюнкт правой части.