proxytuple

package
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 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:*"
)

PublicReadRelation / PublicReadSubject — the only NON-hierarchical pair the proxy accepts: «anybody reads this module's resource».

Publicness is expressed by the tuple itself: kacho-registry writes `user:* #v_get @registry_repository:<reg>/<repo>` and that tuple IS the visibility an anonymous pull resolves — there is no separate flag. Without the pair the intent would be emitted and refused whole, and a public repository would not exist under any configuration.

The narrowing is by SUBJECT, not by relation: the wildcard `user:*` names NO individual recipient, so a module still cannot hand read access to a particular user or service account — that stays with AccessBinding, where it is enumerable, scoped and revocable. Every other constraint (object domain, forbidden types) applies unchanged.

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 reports whether the pair is «anybody reads this resource» (`user:* #v_get`).

It differs from a hierarchical intent in kind: it does not describe the state of the resource (no parent scope, no labels) — it only opens reading. The applying side must therefore treat it as a PURE tuple and leave the resource projection alone: a register would otherwise blank the parent scope and an unregister would delete the projection row of a live resource. Exported so the policy and the apply path share ONE predicate.

func PublicReadObjectTypes

func PublicReadObjectTypes() []string

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

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 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