Help us shape the future of Go! Take the 2026 Go Developer Survey

proxytuple

package
v1.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package proxytuple holds the ONE declaration of what a resource-owning module may write into the authorization model through kaname's FGA proxy — RegisterResource / UnregisterResource. (A third RPC, WriteCreatorTuple, shared this rule and was retired with zero callers — #788.)

WHY THIS LIVES IN THE SHARED FOUNDATION AND NOT UNDER THE OWNER'S `internal/`. The rule has two sides that must never disagree: kaname decides whether to ACCEPT a delivered tuple, and five consumers decide what to EMIT. While the rule lived under `services/iam/internal/`, Go's visibility rule forbade a consumer from importing it, so every consumer knew the rule only as prose — six files repeated it in comments, and the single probe that asserted the receiving side's contract carried a HAND-WRITTEN COPY of the set because importing was impossible. A copy that cannot be compiled against the original drifts silently, and the cost of that drift is not a failing test: a relation the owner refuses is refused on EVERY delivery, the queue classifies the refusal as retryable, and the row wedges its partition for the whole retry window (data-integrity.md §«Межсервисное намерение — контракт ПРИНИМАЮЩЕЙ стороны»). Moving the rule here makes the copy unnecessary: the owner imports it into its accept-check, consumers import it into their intent builders, and the tree gate reads the same declaration.

WHAT THE RULE DECIDES — A TRIPLE, NOT A RELATION. Acceptance is a verdict about «subject, relation, object type» together. A check that knows only the relation set is WRONG in the permissive direction as well as the strict one: public read is expressed by the pair `user:* #v_get`, which no hierarchical relation covers and for which no separate «public» flag exists — the tuple IS the visibility. Do not reintroduce a bare relation-membership gate on the write path; ValidateTuple is the whole rule.

Index

Constants

View Source
const (
	PublicReadRelation Relation = "v_get"
	// Тип назван явно: в группе констант вторая строка без типа унаследовала бы
	// не тип соседа, а нетипизированную константу — и `PublicReadSubject` стал бы
	// строкой там, где рядом стоит `Relation`. Читается как «оба одного типа»,
	// проверяется компилятором как разные.
	PublicReadSubject string = "user:*"
	// PublicReadClassSubject — второе входное слово публикации: класс «аноним»
	// (corelib#116). Написание совпадает с хранимым субъектом публикации у
	// принимающей стороны.
	PublicReadClassSubject string = "system_class:anonymous"
)

PublicReadRelation / PublicReadSubject / PublicReadClassSubject — единственная НЕиерархическая пара, которую принимает прокси: «объект модуля читает всякий».

Публичность выражена самим кортежем: служба реестра пишет `<слово> #v_get @registry_repository:<reg>/<repo>`, и этот кортеж И ЕСТЬ видимость, которую разрешает анонимное чтение, — отдельного флага нет. Без пары намерение было бы отправлено и отвергнуто целиком, и публичного репозитория не существовало бы ни при какой конфигурации.

Входных слов ДВА, перечень закрыт (PublicReadSubjects): подстановка `user:*` — переходное написание, класс `system_class:anonymous` — то, чем публикацию хранит и о чём спрашивает принимающая сторона (corelib#116). Оба слова значат одно — «всякий, в том числе без входа», — поэтому принятое одним снимается другим: хранимая форма публикации у принимающей стороны одна.

Сужение — по СУБЪЕКТУ, а не по отношению: ни одно из слов не называет отдельного получателя, поэтому модуль по-прежнему не может выдать чтение конкретному пользователю или сервисному аккаунту — это остаётся за выдачей прав, где грант перечислим, ограничен областью и отзываем. Класс «вошедший» (`system_class:authenticated`) словом публикации не является: «читает всякий вошедший» — не возможность ресурса. Прочие ограничения (домен объекта, запрещённые типы, закрытый список публикуемых типов) действуют без изменений.

Variables

View Source
var ErrRefused = errors.New("proxy tuple refused")

ErrRefused is the single verdict this rule produces: the tuple is not one this module may write. It carries NO reason — which clause refused is deliberately not observable to the caller (fail-closed, no oracle) — and it is transport-free, so the rule stays importable by a domain layer that may not depend on gRPC (architecture.md dependency rule). The owner maps it to PermissionDenied at its transport boundary, in ONE place, and a test there locks the code and the text.

Functions

func ForbiddenObjectTypes

func ForbiddenObjectTypes() []string

ForbiddenObjectTypes returns the closed forbidden set, sorted. Derived from the map the write path evaluates rather than written out a second time: a hand-written copy of a set cannot be compiled against the original and drifts silently — which is the whole reason the rule was moved into this package. Exported for the tree gate that requires every entry to name a type the model actually declares.

func Hierarchical

func Hierarchical(r Relation) bool

Hierarchical reports whether r is one of the owner-hierarchy relations. It is NOT the accept rule — a tuple also has to satisfy the object-type and domain constraints of ValidateTuple, and a legitimate public-read pair is not hierarchical at all. Exported for the census of the tree gate and for the removal direction (IsProxyWritable), never as a write-path gate on its own.

func IsProxyWritable

func IsProxyWritable(subject, relation string) bool

IsProxyWritable — could THIS relation have been written by a module through the proxy?

The same closed set ValidateTuple decides «accept this write» with, asked in the other direction: «is this one of ours» when the object is torn down. One predicate for both directions — otherwise removal drifts away from acceptance, and it drifts silently: a set that was accepted but not removed is access left behind.

Relations outside the set are deliberately not claimed here: per-object verbs are derived by the reconciler from grants, and removing those is its work, not this path's. A second place deciding the same question is a race and a divergence, not a safety net.

func IsPublicReadGrant

func IsPublicReadGrant(subject, relation string) bool

IsPublicReadGrant — пара есть «объект читает всякий»: `v_get` с одним из входных слов публикации (`user:*` либо `system_class:anonymous`).

От намерения иерархии она отличается родом: состояния ресурса не описывает (ни родительской области, ни меток) — только открывает чтение. Применяющая сторона поэтому обязана считать её ЧИСТЫМ кортежем и не трогать проекцию ресурса: иначе регистрация обнулила бы родительскую область, а снятие удалило бы строку проекции живого ресурса. Экспортирован, чтобы правило и путь применения делили ОДИН предикат — и для обоих слов сразу.

func PublicReadObjectTypes

func PublicReadObjectTypes() []string

PublicReadObjectTypes returns the closed list above, for censuses and gates.

func PublicReadSubjects added in v1.15.0

func PublicReadSubjects() []string

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

func ValidateTuple

func ValidateTuple(callerDomain, subject, relation, object string, opts ...Option) error

ValidateTuple constrains the proxy write path to least privilege: a module writes an owner-hierarchy tuple (plus the publication for anonymous reading — see PublicReadRelation) ONLY onto an object of its own domain. callerDomain is the service short name from the verified mTLS SAN (vpc/compute/nlb/registry); empty (domain unknown) disables the domain binding, while the relation set, the subject narrowing of a publication and of a hierarchy link, and the forbidden object types apply always. Any violation → PermissionDenied, fail-closed, without leaking which clause refused.

Types

type Option

type Option func(*policy)

Option — необязательная настройка правила.

Вариативная форма, а не второй аргумент: подпись ValidateTuple читают семь служб и пять наборов проб, и её смена ради необязательной величины сделала бы ломающим изменением то, что ломающим не является.

func WithTypeOwner

func WithTypeOwner(o TypeOwner) Option

WithTypeOwner подаёт правилу словарь владения типом.

Не подан — владение судится ПРИСТАВКОЙ, как и раньше. Сделав словарь обязательным, мы отвергли бы всё у всякого вызывающего, его не завёдшего, — причём отказом ОПАКОВЫМ by design, то есть с худшей возможной диагностикой.

type Relation

type Relation string

Relation is one relation name of the authorization model as it appears on the wire of a proxy write. It is a distinct type rather than a bare string so a consumer's intent builder names the SAME constant the owner's accept-check evaluates; `string(RelationProject)` is a constant expression, so a consumer keeps its own untyped constant without re-typing the value.

const (
	RelationProject Relation = "project"
	RelationParent  Relation = "parent"
	RelationOwner   Relation = "owner"
)

The owner-hierarchy relations a module may write through the proxy. ONLY ownership / parent links: the resource belongs to a scope (`project`, `parent`) or was created by somebody (`owner`). Privilege relations (`system_admin`/`admin`/`editor`/`viewer`/`v_*`/`fga_writer`/`use`) are absent on purpose — those are authored by the AccessBinding flow, where a grant is enumerable, scoped and revocable, never by a module speaking for itself.

`account` USED TO BE HERE AND IS NOT, and its absence is load-bearing rather than an oversight. No module owns a resource whose containment pointer is an account: iam writes its own account links directly, on its own object types, without the proxy. An accepted relation nobody emits cannot be observed either working or broken — nothing reaches it — while the next reader takes it for a live capability of the product and builds a resource on it. Held by TestEveryAcceptedRelationHasAnEmitter, which reddens on any entry that loses its producer, and by TestProxyRegistrationTriplesAreAcceptedByOwner, which reddens with a coordinate the moment somebody emits a relation that is not here — so putting the tier back when a resource actually needs it is a deliberate edit.

func HierarchicalRelations

func HierarchicalRelations() []Relation

HierarchicalRelations returns the accepted owner-hierarchy relations, sorted the way they are declared. Consumers of the census (gates, reports) get a copy.

type TypeOwner

type TypeOwner interface {
	CatalogModuleOfObjectType(objType string) (string, bool)
}

TypeOwner — ПОРТ: чей это тип объекта. Владельца подаёт ВЫЗЫВАЮЩИЙ.

Порт, а не таблица здесь: полный перечень типов живёт в закрытой таблице iam (`services/iam/internal/authzmap`), за границей видимости этого пакета, и вторая его копия разошлась бы с первой МОЛЧА. Пакет импортируют все семь служб, поэтому «перенести правило туда, где перечень виден» невыразимо; вызывающий у ValidateTuple в прод-коде ровно ОДИН, и он в iam — значит подать словарь может он.

Ответ — МОДУЛЬ КАТАЛОГА, а не короткое имя службы: у балансировщика они различны (`nlb` / `loadbalancer`), и сравнение по имени службы не совпало бы для него НИКОГДА, отняв три живых типа. Наблюдаемо это было бы как «ресурс создан, доступа нет».

ok=false означает «платформа этого типа не знает» — и это НЕ отказ: см. ValidateTuple, ветвь возврата к приставке.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL