principalwire

package
v1.3.1 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: 1 Imported by: 0

Documentation

Overview

Package principalwire — ЕДИНСТВЕННОЕ объявление пространства имён личности на проводе (приёмка KAN-WIRE-1, сценарии KAN-W2-05 / KAN-W2-06, предмет `ПР-2`).

Что здесь объявлено и почему в одном месте

Личность арендатора едет от края к слушателю службы набором ключей под общей приставкой. У этого набора было ДВА независимых объявления — своё у края и своё у фундамента, — и общей константы между ними не существовало. Значит переименование одной стороны СОБИРАЛОСЬ ЧИСТО: приёмник, не найдя своих ключей, читал это как «личности нет» и шёл дальше. Отказом это не кончалось, потому что отсутствие личности в этом тракте отказом не является намеренно — фоновые пути ходят без неё.

Здесь объявление одно, поэтому расхождение двух написаний стало невыразимым: переименовать «одну сторону» больше нечего. Носители (`gateway/internal/principalmeta` у края, `pkg/grpcsrv` у фундамента) берут имена отсюда и своих литералов не держат — это проверяет гейт дерева `internal/repohygiene` `TestIdentityWireNamespaceIsDeclaredOnce`.

Три поверхностные формы ОДНОГО логического ключа

  • `Header*` — канонический HTTP-заголовок (к нему приводит `http.Header`);
  • `HeaderGRPCMeta*` — он же под мостовой приставкой: входящий HTTP-заголовок `Grpc-Metadata-Foo` приезжает слушателю метаданным `foo`;
  • `Meta*` — нижнерегистровый ключ метаданных gRPC, который читает слушатель.

Пары «заголовок ↔ метаданное» держатся вместе не соглашением, а пробой `TestKeyFormsAreOneKeyInThreeSurfaces`: метаданное обязано быть нижним регистром своего заголовка, а заголовок — каноническим.

Почему пакет лежит в фундаменте, а не у края

Направление зависимостей одно: край импортирует фундамент, обратного пути нет by construction (каталог края закрыт `internal`). Объявление, лежащее у края, фундаменту недоступно — а читает ключи именно он.

Чего здесь НЕТ намеренно

Здесь нет ПОЛИТИКИ: что край вычищает у клиента, что кладёт аннотатор, что пропускает мост — решения края, и живут они у края. Здесь — только имена и закрытый каталог свойств, о которых обеим сторонам надо договориться.

Index

Constants

View Source
const (
	Namespace    = "x-kacho-"
	BridgePrefix = "grpc-metadata-"
)
View Source
const (
	MetaPrincipalPrefix = "x-kacho-principal-"
	MetaTokenPrefix     = "x-kacho-token-" // #nosec G101 -- приставка имени ключа метаданных, не удостоверение
)

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

View Source
const (
	HeaderPrincipalType    = "X-Kacho-Principal-Type"
	HeaderPrincipalID      = "X-Kacho-Principal-Id"
	HeaderPrincipalDisplay = "X-Kacho-Principal-Display-Name"
	HeaderTokenACR         = "X-Kacho-Token-Acr"    // #nosec G101 -- имя HTTP-заголовка (утверждение `acr`), не удостоверение
	HeaderTokenJti         = "X-Kacho-Token-Jti"    // #nosec G101 -- имя HTTP-заголовка (утверждение `jti`), не удостоверение
	HeaderTokenScope       = "X-Kacho-Token-Scope"  // #nosec G101 -- имя HTTP-заголовка (утверждение `scope`), не удостоверение
	HeaderTokenExp         = "X-Kacho-Token-Exp"    // #nosec G101 -- имя HTTP-заголовка (утверждение `exp`), не удостоверение
	HeaderTokenAMR         = "X-Kacho-Token-Amr"    // #nosec G101 -- имя HTTP-заголовка (перечень способов подтверждения), не удостоверение
	HeaderTokenMfaAt       = "X-Kacho-Token-Mfa-At" // #nosec G101 -- имя HTTP-заголовка (момент подтверждения), не удостоверение

	// HeaderTokenBasicCredentialID — идентификатор СТРОКИ базового
	// удостоверения, а не сама строка: значение секрета этот заголовок не несёт.
	HeaderTokenBasicCredentialID = "X-Kacho-Token-Basic-Credential-Id" // #nosec G101 -- идентификатор строки, не удостоверение
)

Канонические имена HTTP-заголовков.

View Source
const (
	HeaderGRPCMetaPrincipalType    = "Grpc-Metadata-" + HeaderPrincipalType
	HeaderGRPCMetaPrincipalID      = "Grpc-Metadata-" + HeaderPrincipalID
	HeaderGRPCMetaPrincipalDisplay = "Grpc-Metadata-" + HeaderPrincipalDisplay
	HeaderGRPCMetaTokenACR         = "Grpc-Metadata-" + HeaderTokenACR
	HeaderGRPCMetaTokenJti         = "Grpc-Metadata-" + HeaderTokenJti
	HeaderGRPCMetaTokenScope       = "Grpc-Metadata-" + HeaderTokenScope
)

Мостовые формы тех же заголовков. Собираются приставкой, а не переписываются руками: вторая запись имени разошлась бы с первой молча.

View Source
const (
	MetaPrincipalType    = "x-kacho-principal-type"
	MetaPrincipalID      = "x-kacho-principal-id"
	MetaPrincipalDisplay = "x-kacho-principal-display-name"

	// MetaPrincipalDisplayBin — ДВОИЧНАЯ форма отображаемого имени.
	//
	// Значение обычного ключа метаданных ограничено печатаемой латиницей, и имя,
	// записанное кириллицей, роняет ВЕСЬ вызов, не дойдя до обработчика. Продукт
	// русскоязычный, поэтому имя едет формой, допускающей произвольные байты.
	// Тип и идентификатор остаются обычными ключами: они латиница by construction.
	MetaPrincipalDisplayBin = "x-kacho-principal-display-name-bin"

	MetaTokenACR   = "x-kacho-token-acr"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenJti   = "x-kacho-token-jti"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenScope = "x-kacho-token-scope"  // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenAMR   = "x-kacho-token-amr"    // #nosec G101 -- имя ключа метаданных, не удостоверение
	MetaTokenMfaAt = "x-kacho-token-mfa-at" // #nosec G101 -- имя ключа метаданных, не удостоверение

	// MetaTokenBasicCredentialID — нижнерегистровая форма
	// [HeaderTokenBasicCredentialID]. Объявлена не ради потребителя за краем —
	// его нет, — а ради ЗАПРЕТА: набор ключей края ведётся этими именами, и
	// ключ, которого в нём нет, мост пропустил бы молча.
	MetaTokenBasicCredentialID = "x-kacho-token-basic-credential-id" // #nosec G101 -- идентификатор строки, не удостоверение
)

Нижнерегистровые ключи метаданных gRPC: ровно то, что читает слушатель (`metadata.MD` приводит имя к нижнему регистру).

View Source
const ImportPath = "github.com/PRO-Robotech/corelib/principalwire"

Namespace — приставка КАЖДОГО ключа личности и контекста удостоверения на проводе. Всё под ней принадлежит краю по определению: значение выводится из проверенного удостоверения, и клиент не вправе его прислать.

BridgePrefix — приставка моста REST→gRPC: входящий заголовок `Grpc-Metadata-Foo` приезжает слушателю метаданным `foo`. Обе поверхностные формы одного ключа обязаны судиться одинаково любой политикой над пространством. ImportPath — путь импорта ЭТОГО пакета.

Стоит здесь затем, чтобы проверки дерева опознавали читателя ключа по ПОЛНОМУ пути импорта, а не по имени пакета: псевдоним (`pw "…/pkg/principalwire"`) пишется так же коротко, и разбор по имени обходился бы одной буквой в объявлении импорта.

Variables

This section is empty.

Functions

func Bare

func Bare(key string) string

Bare — голая нижнерегистровая форма имени: нижний регистр и снятая мостовая приставка. Пространству имён может и не принадлежать.

func IsEdgeOnly

func IsEdgeOnly(meta string) bool

IsEdgeOnly — остаётся ли ключ на краю (мост его не пропускает).

Имя приходит нормализованным: нижний регистр, без мостовой приставки — так его отдаёт NamespaceKey, и обе поверхностные формы сводятся к одному имени ещё до этого вопроса.

func IsIdentityShaped

func IsIdentityShaped(bare string) bool

IsIdentityShaped — имеет ли голое имя ФОРМУ ключа личности НЕЗАВИСИМО от того, чьё пространство имён над ним стоит: `x-<обозначение>-principal-<поле>` либо `x-<обозначение>-token-<поле>`.

Зачем свойство ФОРМЫ, когда есть своё пространство имён

Переименование пространства — ровно тот переход, ради которого написан этот пакет, и в окне перехода край и служба собраны из разных деревьев. Приёмник, умеющий спросить только про СВОЮ приставку, о чужой не узнаёт ничего: пересланная личность для него не приезжает вовсе, и он читает это как законную безымянность. Форма — единственный признак, по которому «личность приехала под именем, которого я не читаю» отличимо от «личности не было».

Признак нарочно узкий: он требует и подсемейства (`principal-`/`token-`), и непустого поля за ним. Соседи по проводу (`x-request-id`, `x-forwarded-for`, `x-envoy-…`) под него не подпадают, поэтому отказ не приходит на чужую служебную разметку.

func IsOurs

func IsOurs(bare string) bool

IsOurs — принадлежит ли голое имя пространству имён ЭТОЙ сборки.

func NamespaceKey

func NamespaceKey(key string) (string, bool)

NamespaceKey приводит входящее имя заголовка или ключа метаданных к голой нижнерегистровой форме и сообщает, принадлежит ли оно НАШЕМУ пространству. Мостовая приставка снимается первой, поэтому `Grpc-Metadata-X-Kacho-Admin` и `x-kacho-admin` дают одно и то же ("x-kacho-admin", true).

Types

type Key

type Key struct {
	// Name — имя записи для отказов и переписей. Не поверхностная форма.
	Name string
	// Ident — ИМЯ КОНСТАНТЫ этого пакета, несущей форму Meta.
	//
	// Записано данными затем, что после сведения объявления в одно читатели
	// пишут не литерал, а `principalwire.<Ident>` — и проверка, знающая только
	// литерал, перестала бы видеть читателя вовсе: ни красного, ни зелёного,
	// молчание. Соответствие имени значению держит проба
	// `TestCatalogueIdentsNameRealConstants`: она разбирает исходник этого же
	// пакета, поэтому расхождение невыразимо, а не запрещено соглашением.
	// Пусто, когда формы Meta нет.
	Ident string
	// Meta — нижнерегистровый ключ метаданных. Пусто, когда формы нет.
	Meta string
	// Header — канонический HTTP-заголовок. Пусто, когда формы нет
	// (`…-display-name-bin` живёт только метаданным).
	Header string

	// Fundament — фундамент (`pkg/grpcsrv`) читает этот ключ СВОИМ объявлением.
	//
	// Свойство объявлено ЗДЕСЬ, а держится гейтом дерева: ключ, помеченный
	// читаемым и не заведённый у фундамента, роняет проверку с ИМЕНАМИ ОБОИХ
	// файлов. Ровно тот рассинхрон, который прежде собирался чисто.
	Fundament bool

	// EdgeOnly — край производит ключ ДЛЯ СЕБЯ и за мост не пускает.
	//
	// Запись существует, чтобы односторонность была РЕШЕНИЕМ, а не умолчанием:
	// мост снимает приставку сам, поэтому голая форма пересекает его наравне с
	// мостовой, и «мостовой формы нет» односторонности не даёт.
	EdgeOnly bool
}

Key — одна запись КАТАЛОГА: логический ключ во всех формах, в которых он существует, плюс два свойства, о которых обе стороны провода обязаны договориться.

func Keys

func Keys() []Key

Keys возвращает копию каталога. Копия, а не срез-хранитель: каталог — объявление, и вызывающий, дописавший в него запись, объявлял бы контракт из своего пакета.

type WireNameKind

type WireNameKind int

WireNameKind — чем является строковое значение по отношению к пространству имён личности. Нужен проверкам дерева, чтобы отказ называл предмет, а не «строка похожа на нашу».

const (
	// WireNameNone — к пространству имён личности значение не относится.
	WireNameNone WireNameKind = iota
	// WireNameNamespace — само пространство имён.
	WireNameNamespace
	// WireNameFamily — приставка подсемейства (`x-<обозначение>-principal-`,
	// `x-<обозначение>-token-`). На ней ключуется вычистка чужого ввода, поэтому
	// второе её объявление так же опасно, как второе объявление ключа.
	WireNameFamily
	// WireNameKey — ключ формы личности.
	WireNameKey
)

Виды имён пространства.

func ClassifyWireName

func ClassifyWireName(v string) WireNameKind

ClassifyWireName относит строковое значение к пространству имён личности.

Почему признак СТРУКТУРНЫЙ, а не «начинается с нашей приставки»

Переименование пространства — тот самый переход, ради которого написан этот пакет. Признак, знающий только сегодняшнюю приставку, не увидел бы второго объявления, сделанного уже под новой, — то есть промолчал бы ровно в тот день, когда нужен.

Чего признак НЕ относит к пространству — названо, а не спрятано

Общая приставка ещё не делает имя ключом личности: `x-kacho-hook-token` (общий секрет крючка службы личности), `x-kacho-admin`, `x-kacho-project-id` — законные имена вне этого контракта, и запрет объявлять их значил бы запрет на соседние подсистемы. Различает подсемейство и непустое поле за ним.

Jump to

Keyboard shortcuts

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