Documentation
¶
Overview ¶
access_binding_scope.go — typed Scope enum for the RBAC v2 AccessBinding shape. The Scope tier anchors the binding in the cluster ▶ account ▶ project hierarchy; per-resourceName grants emit direct FGA tuples that respect Scope only as a sanity guard.
client_assertion.go — доменные понятия аутентификации клиента подписанным утверждением (задача kacho#898, приёмка F2).
Что здесь и чего здесь нет ¶
Здесь — понятия, о которых обязаны договориться ВСЕ слои: чем клиент себя называет, из какого реестра он разрешается, и чем отличается повтор от первого предъявления. Разбор подписи, чтение базы и форма HTTP-ответа живут у своих слоёв: домен о них не знает (чистый Go, только stdlib).
Package domain — entities + value-types for kaname.
The domain layer depends on stdlib + multierr only — never on pgx, grpc-stubs, or sqlc. `kacho-proto` is permitted strictly for envelope types (Operation, status); for concrete IAM proto-structs we have `internal/dto/toproto`.
constants_extended.go — id-префиксы и константы. Стиль формата отличается от corelib `ids.NewID` (3-char prefix + 17-char crockford-base32) — здесь используется `<prefix>_<17-char crockford>` с `_` separator, чтобы префиксы могли быть длиннее 3 символов (`cag`, `org`, `cond`, `evt`, `soc`).
DB CHECK constraints в миграциях 0011..0014 enforce'ат соответствие формату per-таблица (например `^cag_[0-9a-hjkmnp-tv-z]{17}$`).
federated_assertion.go — доменные понятия перечня доверенных издателей утверждения (задача kacho#1124).
Что этот перечень такое и чем он отличается от реестра клиентов ¶
Реестр клиентов отвечает на вопрос «наша ли это строка и её ли ключ». Перечень доверенных издателей отвечает на другой: «согласны ли МЫ, что вот этот ПОСТОРОННИЙ издатель вправе поручиться вот за этот свой субъект». Ключевой материал здесь принадлежит не нам, и решение о доверии — единственное, что у нас есть.
Отсюда две вещи, которые верны здесь и неверны у реестра клиентов:
- **запись доверия имеет собственный срок.** У клиента срок означает «эта строка больше не выдаёт»; здесь — «мы больше не ручаемся за постороннего». Снятие доверия сроком обязано работать, иначе выданное однажды доверие переживёт то, ради чего выдавалось;
- **пустой перечень означает «не доверяем никому».** Величина, которая может быть пустой и не проверена на непустоту, означает «принимаем любого» — класс, записанный в корпусе про круг законных отправителей. Тут он даёт не «приняли лишний заголовок», а токен платформы предъявителю, которого мы не заводили вовсе.
ids_extended.go — IAM-style id generator.
Формат: `<prefix>_<17-char crockford-base32>` (lowercase, без I/L/O/U). Отличается от corelib `ids.NewID` тем, что префикс может быть длиннее 3-х символов (DB CHECK constraints в миграциях 0011..0014).
Источник энтропии — crypto/rand. 17 символов crockford × 5 бит = 85 бит.
limit.go — СЛОВАРЬ ВИДОВ, и только он. Авторитета величин здесь больше нет.
Задача продукта `PRO-Robotech/kacho#2117`, приёмка `KAN-QUOTA-1`, стадия S4, сценарий `KAN-Q4-14`, условие готовности `DoD S4` пп. 13-14.
───────────────────────────────────────────────────────────────────────────── ЧТО ОТСЮДА УШЛО И ПОЧЕМУ ЭТО НАЗВАНО, А НЕ СТЁРТО МОЛЧА
Файл держал авторитет величин целиком: идентичность потолка (`LimitID`), его область (`LimitScope` с тремя рукавами и старшинством), носителя (`LimitCarrier`), ЗАКРЫТЫЙ КАТАЛОГ двадцати четырёх видов (`countableKinds`, `CountableKind`) и разрешение действующей величины между областями (`Limit`, `LimitFilter`, `EffectiveLimit`, `ResolveEffective`).
Всё это — предмет ДРУГОГО продукта: служба доступа отвечает на вопрос «кому что разрешено», а не «сколько чего можно завести». Оператор, ставящий её в своём облаке, получал вместе с ней авторитет величин, которого не просил и которым не управлял.
───────────────────────────────────────────────────────────────────────────── ЧТО ОСТАЛОСЬ — И ПОЧЕМУ СНЯТЬ ЕГО ЗАОДНО БЫЛО БЫ РАСШИРЕНИЕМ ПОВЕРХНОСТИ
Остался СЛОВАРЬ: имя вида и разбор его частей. Служба по-прежнему СЧИТАЕТ три собственных вида (`iam.account`, `iam.user.credential`, `iam.serviceAccount.credential`) и по-прежнему отвечает арендатору, каков его потолок. Сменился ИСТОЧНИК величины: её объявляет посадка (`limit_posture_stated.go`, `П25`), а не внешний авторитет, которого в самостоятельной установке нет by construction.
Снять словарь вместе с авторитетом значило бы оставить установку без ограничения на число аккаунтов и путей входа — расширение поверхности, не названное ни в одном артефакте.
───────────────────────────────────────────────────────────────────────────── ЗАКРЫТОГО КАТАЛОГА ЗДЕСЬ БОЛЬШЕ НЕТ, И ЭТО ПРОВЕРЯЕТСЯ, А НЕ ОБЕЩАЕТСЯ
`internal/check` `TestLimitAuthorityIsGoneFromTheTree` судит узлы-имена прод-дерева и объявление `countableKinds` отдельной осью: возврат каталога — находка с координатой, а не тихое отрастание. Куда каталог уезжает, решает развилка платформы `kacho#2190`, и служба доступа её за владельца не решает.
oauth_ceremony.go — СОБСТВЕННАЯ ЦЕРЕМОНИЯ OAuth: код авторизации, семейство выданного по нему и обновляющий токен (kaname#313).
Домен здесь описывает ЗНАЧЕНИЯ и ИСХОДЫ; хранение, свёртки и операторы живут у слоя доступа. Чистый Go, только stdlib.
Исходов ПРЕДЪЯВЛЕНИЯ здесь нет, и это не пропуск ¶
Обмен кода и оборот токена обновления исполняет движок фундамента (`corelib/oauthceremony`) над хранилищами слоя доступа, и исходы предъявления называет контракт его портов: «записи нет», погашен, обёрнут. Прежняя тройка исходов обмена и ротации принадлежала композициям слоя доступа, у которых прод-вызывающих не было, и снята вместе с ними (kaname#434). Здесь остаются исходы, которых у порта нет: заведение семейства в сессию, отсечка субъекта предъявленной строки, запись выпуска, занятость проверяющего секрета.
principal_claims.go — состав утверждений, называющий ПРИНЦИПАЛА, за которого говорит выпущенный нами токен.
Почему имена объявлены, а не выписываются по месту ¶
Читателей у этих имён больше одного: их СТАВИТ чеканка и их ЧИТАЕТ приёмная сторона. Пока имена живут литералами по своим пакетам, их различие не является ничьей находкой — оно не выражено и потому не может покраснеть. Разойтись же им есть чем, и разошлись бы они МОЛЧА: переименование на выпуске оставило бы читателя без принципала не отказом, а тишиной — токен проверился бы целиком, а за кого он говорит, читатель не узнал бы. Отличить такое состояние от «токен выдан системной личности» на стороне читателя нечем.
Что означает отсутствие каждого ¶
Вид и идентификатор ОБЯЗАТЕЛЬНЫ: «назвать некого» и «назван системный» — разные состояния, и смешивать их нельзя, потому что системная личность и есть та, которой принадлежат все служебные записи. Отображаемое имя косметическое: оно ни на одно решение не влияет и при отсутствии замещается идентификатором.
Index ¶
- Constants
- Variables
- func AccountScopeApplies(k LimitKind) bool
- func AllMaterializableTypes() []string
- func BindableScopes() []string
- func CeremonyFamilyBound(born, sessionExpiresAt time.Time, familyTTL time.Duration) time.Time
- func CeremonyScopes() []string
- func ClassNestedRuleRefusal(role RoleID, rules Rules, containers map[string]PublicReadContainer) (string, bool)
- func CustomDefinitionTierToScope(tierType, tierID string) (account, project string, ok bool)
- func CutoffBelowFirstAuthentication(first time.Time) time.Time
- func DerivedIDSuffix(seed string) string
- func EncodeRules(rs Rules) ([]byte, error)
- func FGASubjectRef(subjectType, subjectID string) string
- func HasSystemClass(subjects []Subject) bool
- func HumanSessionEndReasons() []string
- func IsAssertionClientUnknown(err error) bool
- func IsAssertionReplayed(err error) bool
- func IsClassReadVerb(verb string) bool
- func IsClassSubjectWord(word string) bool
- func IsIdentityCarriedKind(k LimitKind) bool
- func IsLabelSelectableType(objectType string) bool
- func IsPersonalAccountName(name AccountName) bool
- func IsPostureStatedKind(k LimitKind) bool
- func IsPublicReadVerb(verb string) bool
- func IsRetiredType(objectType string) bool
- func IsRoleAssignable(r Role, resourceType, resourceID string) bool
- func IsRoleAssignableInAccount(r Role, resourceType, resourceID, scopeOwningAccountID string) bool
- func IsScopeAnchorKind(bare string) bool
- func IsSeededResourceID(id string) bool
- func IsStoredPublication(subject, relation string) bool
- func IsSystemClassName(id SubjectID) bool
- func IsTrustedIssuerUnknown(err error) bool
- func IsVerbOfType(verb string, typeVerbs []string) bool
- func IsWellFormedModuleName(module string) bool
- func IsWellFormedObjectTypeName(objectType string) bool
- func ModuleNameGrammar() string
- func ModuleOfCatalogType(dotted string) string
- func NestedRuleBeyondDirectChildText(containerType string, role RoleID, nestedType string) string
- func NestedRuleNotWholeText(role RoleID, childType string) string
- func NewKac127ID(prefix string) string
- func NormalizeVerb(v string) string
- func ObjectTypeNameGrammar() string
- func OrderVerbsForDisplay(verbs []string) []string
- func PasswordHashFormatMarkers() []string
- func RefuseBasicCredential(reason BasicCredentialRefusalReason) error
- func ResolveVerbsAndTier(authored, typeVerbs []string) (verbs []string, tier string)
- func RetiredTypes() []string
- func RoleBoundToSystemClassText(id RoleID) string
- func RoleDeclaringModule(r Role) string
- func RoleNotReadOnlyForClassText(id RoleID) string
- func RuleRefsByRule(rules Rules) [][]RoleRuleRef
- func ScopeTierByKind() map[string]Scope
- func ScopeTypeFromDotted(dotted string) (bare string, ok bool)
- func ScopeTypeToDotted(bare string) string
- func SeedIdentityDeclared(name string) string
- func SeedIdentitySpellings(name string) []string
- func SeededResourceIDs() []string
- func SpeakerClasses(speaker string) []string
- func SpeakerRefusal(speaker string) error
- func UndeclaredContainerText(targetType string, role RoleID, childType string) string
- func ValidKeyIDForm(raw string) bool
- func ValidTargetType(cat TargetTypeCatalog, dotted string) bool
- func ValidateAssertionID(raw string) error
- func ValidateCeremonyDigest(field, digest string) error
- func ValidateCeremonyLevel(level string) error
- func ValidatePKCEChallenge(challenge, method string) error
- func ValidateRedirectURIs(field string, uris []string, required bool) error
- type AccessBinding
- type AccessBindingID
- type AccessBindingStatus
- type AccessKey
- type AccessKeyChallenge
- type AccessKeyChallengePurpose
- type AccessKeyChallengeState
- type AccessKeyDescription
- type AccessKeyID
- type AccessKeyLoginChallenge
- type AccessKeyName
- type AccessTarget
- func (t AccessTarget) Contains(dottedType, id string) bool
- func (t AccessTarget) Digest() string
- func (t AccessTarget) DistinctTypes() []string
- func (t AccessTarget) FirstNonLiveType(cat TargetTypeCatalog) (string, bool)
- func (t AccessTarget) IsEmpty() bool
- func (t AccessTarget) ResourceIDsForTypes(types []string) []string
- func (t AccessTarget) Validate() error
- type Account
- type AccountID
- type AccountName
- type Arm
- type AssertionClient
- type AssertionClientKind
- type AssignableRole
- type AuditEventID
- type AuditOutboxEntry
- type AuditOutboxStatus
- type BasicCredential
- type BasicCredentialRefusalReason
- type BearerDigest
- type CeremonyContext
- type CeremonyHandle
- type ClientDescription
- type Cluster
- type ClusterAdminEntry
- type ClusterAdminGrant
- type ClusterAdminGrantID
- type ClusterID
- type ClusterName
- type CodeDigest
- type ContainerSelector
- type CredentialKind
- type Description
- type Discoverability
- type DisplayName
- type Email
- type EmailChangeCode
- type EmailChangeCodeID
- type EventTypeName
- type ExternalSubject
- type FamilyRevocationReason
- type FeedSource
- type FormKind
- type GrantDelegation
- func (d GrantDelegation) Active() bool
- func (d GrantDelegation) AllowsRole(id RoleID) bool
- func (d GrantDelegation) AllowsSubjectType(t SubjectType) bool
- func (d GrantDelegation) AllowsTargetType(dotted string) bool
- func (d GrantDelegation) IsDelegate(principalType, principalID string) bool
- func (d GrantDelegation) IsDelegateSubject(s Subject) bool
- type GrantDelegationActor
- type GrantDelegationID
- type GrantDelegationStatus
- type GrantSubjectType
- type Group
- type GroupID
- type GroupMember
- type GroupName
- type HumanSession
- type HumanSessionID
- type InteractiveClient
- type InteractiveClientID
- type InteractiveClientName
- type InteractiveClientStatus
- type InviteStatus
- type KeyID
- type LabelKey
- type LabelVal
- type Labels
- type LimitKind
- type LoginMethod
- type LoginMethodKind
- type LoginMethodState
- type LoginVerifier
- func (v LoginVerifier) Format(f fmt.State, verb rune)
- func (v LoginVerifier) GoString() string
- func (v LoginVerifier) IsZero() bool
- func (v LoginVerifier) LogValue() slog.Value
- func (v LoginVerifier) MarshalJSON() ([]byte, error)
- func (v LoginVerifier) MarshalText() ([]byte, error)
- func (v LoginVerifier) Reveal() string
- func (v LoginVerifier) SameMaterial(other LoginVerifier) bool
- func (v LoginVerifier) String() string
- type Membership
- type MembershipID
- type MembershipState
- type MembershipTuple
- type MirrorObject
- type ModuleSet
- type OAuthClientName
- type OperationID
- type PasswordCostClass
- type PasswordHashCostParam
- type PasswordHashFormat
- type PasswordHashFormatRecord
- type PasswordHashParamRange
- type Permission
- type Permissions
- type PrivilegeDerivation
- type Project
- type ProjectID
- type ProjectName
- type PrunedSelectorType
- type PublicReadContainer
- type PublishedKey
- type RecoveryCode
- type RecoveryCodeID
- type RecoveryCodeValue
- func (v RecoveryCodeValue) Digest() CodeDigest
- func (v RecoveryCodeValue) Format(f fmt.State, verb rune)
- func (v RecoveryCodeValue) GoString() string
- func (v RecoveryCodeValue) IsZero() bool
- func (v RecoveryCodeValue) Letter() string
- func (v RecoveryCodeValue) LogValue() slog.Value
- func (v RecoveryCodeValue) MarshalJSON() ([]byte, error)
- func (v RecoveryCodeValue) MarshalText() ([]byte, error)
- func (v RecoveryCodeValue) String() string
- type RecoveryCompletion
- type ResourceRef
- type ResourceType
- type RevocationReason
- type Role
- func (r Role) AuthoredVerbs(lookup TypeVerbLookup) []string
- func (r Role) CanonicalRank() int
- func (r Role) DefinitionTierID() string
- func (r Role) DefinitionTierType() string
- func (r Role) DisplayName() string
- func (r Role) EffectiveVerbs(lookup TypeVerbLookup) []string
- func (r Role) GrantsReadsOnly() bool
- func (r Role) IsClusterAdminRole() bool
- func (r Role) IsSystemDerived() bool
- func (r Role) Purpose() string
- func (r Role) Validate(modules ModuleSet) error
- func (r Role) VerbNotes(lookup TypeVerbLookup) map[string]string
- func (r Role) WithoutComputedState() Role
- type RoleHealth
- type RoleID
- type RoleIntegrity
- type RoleLifecycle
- type RoleLifecycleState
- type RoleName
- type RoleRetirement
- type RoleRuleRef
- type RoleScopeGroup
- type RoleSegment
- type RoleVerb
- type RotatedRefreshToken
- type Rule
- type RuleLabelSelector
- type RuleLifecycle
- type RuleNameHold
- type RulePolicy
- type RuleSelector
- type RuleState
- type Rules
- func (rs Rules) CoversType(dottedType string) bool
- func (rs Rules) HasAnchorRule() bool
- func (rs Rules) HasChildren() bool
- func (rs Rules) LabelSelectors() []RuleLabelSelector
- func (rs Rules) MaterializingSelectors() []RuleSelector
- func (rs Rules) MaterializingSelectorsInScope(scope Scope) []RuleSelector
- func (rs Rules) NamedKeys() []RuleNameHold
- func (rs Rules) ScopeSelfVerbs(scopeResource string, typeVerbs []string) []string
- func (rs Rules) Validate(policy RulePolicy, modules ModuleSet) error
- func (rs Rules) WithoutResourceName(dottedType, id string) (out Rules, removed int, changed bool)
- type SAOAuthClientID
- type Scope
- type ScopeAnchor
- type SeedIdentityRename
- type SelectorPruneOutcome
- type ServiceAccount
- type ServiceAccountID
- type ServiceAccountOAuthClient
- type SessionBearer
- func (b SessionBearer) CookieValue() string
- func (b SessionBearer) Digest() BearerDigest
- func (b SessionBearer) Format(f fmt.State, verb rune)
- func (b SessionBearer) GoString() string
- func (b SessionBearer) IsZero() bool
- func (b SessionBearer) LogValue() slog.Value
- func (b SessionBearer) MarshalJSON() ([]byte, error)
- func (b SessionBearer) MarshalText() ([]byte, error)
- func (b SessionBearer) String() string
- type SessionRevocation
- type SigningAlgorithm
- type SigningKeyRecord
- type SigningKeyState
- type StepUpChallenge
- type StepUpChallengeState
- type StructuralTuple
- type Subject
- type SubjectID
- type SubjectPrivilege
- type SubjectType
- type SubordinateResource
- type SvcAccountName
- type TargetMember
- type TargetTypeCatalog
- type TrustedIssuer
- type TrustedSubject
- type TypeVerbLookup
- type User
- type UserID
- type UserOAuthClient
- type UserOAuthClientID
- type UserTokenRevocation
- type VerificationCode
- type VerificationCodeID
- type VerificationCodeValue
- type VerificationStatus
- type WithdrawnGrant
- type WithdrawnGrantCause
- type WithdrawnGrantSource
Constants ¶
const ( ScopeTypeClusterDotted = "iam.cluster" ScopeTypeAccountDotted = "iam.account" ScopeTypeProjectDotted = "iam.project" )
Dotted scope-type API projection (redesign-2026 F7). The AccessBinding scope-anchor is renamed resource_type/resource_id → scopeType/scopeId on the wire, with the word "resource" freed for the reintroduced target. The wire scopeType is dotted (`iam.{cluster,account,project}`) while the within-service storage keeps the bare kind (`cluster`/`account`/`project`) — the two are mapped at the API boundary (dto on output, handler on input). Only the three hierarchy tiers can anchor a binding, so the mapping is total over them.
const ( PrefixAccount = "acc" PrefixProject = "prj" PrefixUser = "usr" PrefixServiceAccount = "sva" PrefixGroup = "grp" PrefixRole = "rol" PrefixAccessBinding = "acb" // PrefixOperationIAM — separate prefix so api-gateway routes // `OperationService.Get(id)` correctly (by the first 3 characters of the // id). MUST differ from PrefixAccount — otherwise `acc<…>` (Account) and // an Operation on Account would collide. PrefixOperationIAM = "iop" )
ID prefixes for kaname resources. Mirrors the canonical prefix constants in `pkg/ids` so the use-case layer can refer to short names locally (`PrefixAccount`, `PrefixProject`, …) without re-importing corelib for trivial id construction.
const ( PrincipalTypeSystem = "system" PrincipalTypeAnonymous = "anonymous" PrincipalTypeUser = "user" PrincipalTypeServiceAccount = "service_account" PrincipalDisplayBootstrap = "kaname-bootstrap" PrincipalIDBootstrap = "bootstrap" )
PrincipalType — allowed values for kaname.operations.principal_type. 'system' / 'kaname-bootstrap' are used for internal/background flows; 'user' / 'service_account' come from OIDC at the api-gateway edge.
const ( PrefixClusterAdminGrant = "cag" PrefixCondition = "cond" PrefixSAOAuthClient = "soc" PrefixUserOAuthClient = "uoc" PrefixAuditEvent = "evt" // ClusterSingletonID — единственный валидный id для кластера. ClusterSingletonID = "cluster_root" // OwnerRoleID — deterministic id of the net-new `owner` system-role // (RBAC explicit-model 2026), seeded by migration 0035 as // `'rol' || substr(md5('owner'),1,17)`. The Account.Create auto-binding // references it. Kept here so the use-case does not re-hash the name at // runtime (and stays in lockstep with the migration seed id). OwnerRoleID = "rol72122ce96bfec66e2" // ClusterAdminRoleID — deterministic id of the system cluster-admin role // (`admin`, name 'admin'), seeded by migration 0001 as // `'rol' || substr(md5('admin'),1,17)` and re-seeded with its `*.*.*` rules by // migration 0031. This is the canonical "GLOBAL super-admin" role. // // IMPORTANT: the `owner` role (OwnerRoleID) carries the SAME `*.*.*` // wildcard SHAPE as cluster-admin, so the GLOBAL+all exception MUST be // keyed on this PINNED id (+ is_system), not on the shape alone — otherwise // owner would be misclassified as cluster-admin and slip past the reject. ClusterAdminRoleID = "rol21232f297a57a5a74" // SystemAdminRoleID — the hand-rolled deterministic id of the second `*.*.*` // superuser system role (`system.admin`; свод посеял её под именем // `kacho-system.admin`, перевод — миграция 20260913143007, kacho#2554), seeded by migration 0001 and // re-seeded with `*.*.*` rules by migration 0031. Also a legitimate // cluster-admin superuser. SystemAdminRoleID = "rol000000000sysadmin" // SystemViewerRoleID — рукописный детерминированный id второй системной роли // пола каталога (`system.viewer`; свод посеял её под именем `kacho-system.viewer`, // перевод — миграция 20260913143007, kacho#2554), посеянной миграцией 0001. // // ДЛИНА ЕГО — 21, а не 20, и это ФАКТ ПРИМЕНЁННОЙ МИГРАЦИИ, а не описка, // которую можно поправить: id неизменяем на всю жизнь ресурса (ban #15, // «операции смены id НЕ существует»), а применённую миграцию править нельзя // (ban #5). Переименование поздней миграцией сверх того есть перенос выдачи с // одной роли на другую — тихое расширение прав, отвергаемое гейтом // `TestNoMigrationMovesGrantsBetweenRoles`. // // Поэтому литерал назван здесь и стоит в закрытом перечне // `SeededResourceIDs()`: проверка формы принимает то, что продукт сам посеял, // а не объявляет собственный посев негодным. Задача kacho#1808. SystemViewerRoleID = "rol000000000sysviewer" )
id-префиксы. Singleton (`cluster_root`) — литерал, не генерируется. Outbox events (`evt_`) — ULID-based, длина 20..30.
const ( ConditionMFAFresh = "mfa_fresh" ConditionNonExpired = "non_expired" ConditionSourceIPInRange = "source_ip_in_range" ConditionBusinessHours = "business_hours" ConditionDeviceCompliant = "device_compliant" )
Condition expressions whitelist (migration 0012 access_binding_conditions_expression_whitelist_ck).
const ( MaxGrantDelegationRoles = 16 MaxGrantDelegationTargetTypes = 16 MaxGrantDelegationSubjectTypes = 4 )
Пределы осей делегирования — границы размера запроса (Р6 приёмки), а не предел числа ресурсов: число делегирований kaname не ограничивает (Р-В7).
const ( // RevokeReasonLogout — собственный выход человека: причина снятия его // записи сессии и причина отсечки. RevokeReasonLogout = "logout" // RevokeReasonPasswordChange — смена пароля и завершение восстановления: // причина снятия прочих записей сессии и причина отсечки. RevokeReasonPasswordChange = "password-change" // RevokeReasonSecondFactorRemoved — снятие второго фактора самим человеком // гасит ПРОЧИЕ его сессии (Ф12 Р9, kacho#1281): журнал отличает это от // выхода и от смены пароля. Причина снятия записи сессии. RevokeReasonSecondFactorRemoved = "second-factor-removed" // RevokeReasonSecondFactorReset — причина отсечки, которую пишет сброс // второго фактора администратором облака (Ф12 Р10): все сессии человека покрыты // отсечкой `now` существующим писателем принудительного выхода. В словарь // снятия сессии НЕ входит: ни одному снимающему методу она не передаётся. RevokeReasonSecondFactorReset = "second-factor-reset" // RevokeReasonAccessKeysReset — причина отсечки, которую пишет сброс ключей // доступа администратором облака (kaname#638; приёмка // `cloud-administrator-resets-login-methods.md`, Р4): все сессии человека // покрыты отсечкой `now`. Как и у сброса второго фактора, в словарь снятия // сессии НЕ входит: записи сессии она не снимает, а отсекает. RevokeReasonAccessKeysReset = "access-keys-reset" // RevokeReasonAdminForceLogout — выход, произведённый распорядителем // (`InternalIAMService.ForceLogout`; kaname#334, приёмка // `forced-exit-has-its-own-session-end-reason.md`, Р1, Р3). Роли у слова // ДВЕ, и объявлено оно одно: // - причина снятия НАШИХ записей сессии входа принудительным выходом. // Это слово закрытого словаря, и из запроса оно не берётся (Р2): // свободная причина распорядителя в запись сессии не идёт; // - умолчание СВОБОДНОЙ причины отсечки и события, когда запрос // причины не несёт. Совпадение намеренное: предмет у обеих записей // один — принудительный выход. RevokeReasonAdminForceLogout = "admin-force-logout" // RevokeReasonEmailVerified — подтверждение адреса снимает ПРОЧИЕ сессии // человека (kaname#456, Р10 п. 3): обычное положение получает только та // сессия, в которой код предъявлен. Причина снятия записи сессии; отсечки // подтверждение не пишет. RevokeReasonEmailVerified = "email-verified" // RevokeReasonEmailChanged — исход смены адреса снимает ПРОЧИЕ сессии // человека (kaname#635, Р8 п. 4): адрес — канал восстановления, и после // смены канала прежние устройства за учётную запись не держатся; текущая // сессия получает новый носитель. Причина снятия записи сессии; отсечки // смена адреса не пишет. RevokeReasonEmailChanged = "email-changed" // RevokeReasonEndedFromAnotherSession — человек снял свою запись из ДРУГОЙ // своей сессии: выход из выбранной либо из всех, кроме текущей (kaname#634, // приёмка `own-sessions-are-listed-and-ended-by-their-owner.md`, Р6). Причина // снятия записи сессии; отсечки эти глаголы не пишут. RevokeReasonEndedFromAnotherSession = "ended-from-another-session" // RevokeReasonAccessKeyRevoked — снятие ключа доступа гасит сессии // человека (Ф13 Р8, kaname#669): ключ снимают чаще всего потому, что // устройства лишились, и живая сессия на нём — сессия держателя устройства. // Причина снятия записи сессии и причина отсечки. RevokeReasonAccessKeyRevoked = "access-key-revoked" // RevokeReasonIdentityRemoved — снятие личности (`UserService.Delete`) // обрывает всё, выданное ей до момента снятия (kaname#664, Р5). Только // причина отсечки: записи сессии уходят каскадом той же транзакции и // причиной снятия не помечаются. RevokeReasonIdentityRemoved = "identity-removed" // RevokeReasonIdentityBlocked — блокировка личности (`UserService.Block`) // обрывает всё, выданное ей до момента блокировки, и снятие блокировки // выданного до неё не возвращает (kaname#717). Только причина отсечки: // записи сессии блокировка не снимает, а отсекает. RevokeReasonIdentityBlocked = "identity-blocked" // RevokeReasonAddressClaimed — запись с неподтверждённым адресом перешла к // владельцу ящика, предъявившему код восстановления (kaname#458, Р3 п. 3, // Р7): все сессии записи сняты, выданное до перехода отсечено. Журнал // отличает это от смены пароля самим человеком — у оператора это разные // вопросы. Причина снятия записи сессии и причина отсечки. RevokeReasonAddressClaimed = "address-claimed" )
Слова причин ДВУХ записей — одним объявлением (Ф3 Р4, Р6, Р9).
Константы ниже служат двум словарям. Закрытый — причина СНЯТИЯ записи сессии: ограничение `human_sessions_ended_reason_check`, его перечень в домене — HumanSessionEndReasons. Открытый — причина ОТСЕЧКИ (`user_token_revocations.reason`, ограничена только длиной). Какой записи служит каждая константа, сказано у неё. Слово, которое пишут обе записи, объявлено один раз: второе написание разошлось бы с первым молча.
`password-change` — ТО ЖЕ значение, что пишет существующий хук завершения восстановления (`internal/apps/kaname/api/user`). `logout` — своё: журнал обязан отличать выход от смены пароля (Ф1-63).
const ( // ClaimPrincipalType — вид принципала: `user` либо `service_account`. ClaimPrincipalType = "kaname_principal_type" // ClaimPrincipalID — идентификатор принципала. ClaimPrincipalID = "kaname_principal_id" // ClaimPrincipalDisplay — отображаемое имя принципала. Косметическое. ClaimPrincipalDisplay = "kaname_principal_display_name" // ClaimSAKeyID — идентификатор строки удостоверения служебной учётки, по // которому выдан токен (kaname#947). Это КЛЮЧ ОТСЕЧКИ: снятие строки пишет // отсечку её идентификатором (триггер схемы // `sa_oauth_client_removal_cuts_minted_tokens`), и правило отзыва // (`tokenrevocation`) читает его из состава. Токен, выданный по // удостоверению и не несущий этого имени, отзыв удостоверения не снимает — // он действует до своего срока. ClaimSAKeyID = "kaname_sa_key_id" )
const ( // MaxRules — rules[] cardinality per role. MaxRules = 64 // MaxCompiledPermissions — compiled-permissions cap. // Lockstep with DB CHECK + proto (size). MaxCompiledPermissions = 1024 )
const ( // SystemAccountName — имя системного аккаунта, в котором живут служебные // записи модулей. SystemAccountName = "system" // SystemAdminRoleName — имя встроенной роли администратора установки. SystemAdminRoleName = "system.admin" // SystemViewerRoleName — имя встроенной роли пола каталога. SystemViewerRoleName = "system.viewer" // BootstrapAdminSAName — имя служебной записи, которой чеканят первый // неинтерактивный токен. BootstrapAdminSAName = "bootstrap-admin" )
Объявленные написания посевной идентичности (§2.3 приёмки). Имя называет УСТАНОВКУ, а не продукт: у стороннего оператора ею не владеет ни платформа, ни служба, поэтому различитель в имени лгал бы в обе стороны.
Названы здесь, рядом с окном, а не у каждого читателя: иначе объявлений стало бы столько же, сколько читателей, и разошлись бы они молча.
const ( SpeakerAnonymous = string(SubjectTypeSystemClass) + ":" + string(ClassAnonymous) SpeakerAuthenticated = string(SubjectTypeSystemClass) + ":" + string(ClassAuthenticated) )
Субъекты классов в записи модели прав (`тип:идентификатор`).
const AccessKeyChallengeBytes = 32
AccessKeyChallengeBytes — длина случайного испытания: 32 байта = 256 бит, не меньше 16 требуемых нормой (§13.4.3).
const AccessKeyDescriptionMax = 256
AccessKeyDescriptionMax — предел описания в ЗНАКАХ, единица схемы (`CHECK (length(description) <= 256)`), как у удостоверений-соседей.
const AccessKeyLoginChallengeContextMax = 128
AccessKeyLoginChallengeContextMax — предел длины контекста формы: контекст — 32 случайных байта в base64url без дополнения (43 знака); предел с запасом держит ограничение схемы, а не доверие к вызывающему.
const CarrierIdentity = "identity"
CarrierIdentity — носитель «личность» в том виде, в каком он стоит в схеме.
Значение принадлежит СХЕМЕ (`project_resource_quotas.carrier_type` и предикат списывающего триггера), поэтому у адаптера базы свой литерал, а не ссылка сюда: адаптер обязан называть то, что лежит в столбце, даже если домен однажды назовёт это иначе. Совпадение трёх написаний закреплено пробой (`internal/repo/kaname/pg`), а не подразумевается.
const CatalogEndpoint = "GET /iam/v1/permissionCatalog"
CatalogEndpoint — where a client reads the grantable taxonomy AND the retired resources with their successors.
ОБЪЯВЛЕНИЕ ОДНО НА ДЕРЕВО. Адрес называют ОБЕ полосы отказа на несуществующем ресурсе — снятия (здесь) и грантуемости (`api/role/rules_catalog.go`), — и до kacho#1814 первая молчала: различие никем не решалось, оно побочный эффект того, что гейты писались порознь.
Адрес назван в отказе потому, что каноническое написание намеренно НЕ единообразно по модулям (`compute.instance` / `iam.serviceAccount` единственного числа; `storage.volumes` / `registry.registries` / `loadbalancer.networkLoadBalancers` множественного), поэтому «догадайся» исполнимой инструкцией не является.
const CeremonyHandleBytes = 64
CeremonyHandleBytes — длина рукоятки: верхняя граница нормы для `user.id`.
const CeremonyScopeOpenID = "openid"
CeremonyScopeOpenID — область интерактивного входа: её запрашивает первопартийный клиент (консоль, CLI), и она проецируется в утверждение `scope` выданного токена доступа (RFC 9068 §2.2.3).
Прав область НЕ несёт: решение о доступе принимает модель (приёмка LINE-A-1 Р9), и выданное по коду судится так же, как всякий наш токен. Токена личности церемония не выдаёт (`corelib/oauthceremony`, doc.go) — область называет вид гранта, а не обещание ID-токена.
const ClientDescriptionMaxRunes = 512
ClientDescriptionMaxRunes — предел хранимого описания в рунах (Р3).
const CustomRoleNameForm = `^[a-z][a-z0-9_]{0,40}$`
Regex / limits — centralised so domain.Validate and the DB CHECKs agree.
Формы имени ресурса здесь БОЛЬШЕ НЕТ: она объявлена один раз на всё дерево (`pkg/validate/nameform`), и iam читает то же объявление (kacho#1279). Прежде здесь стояла своя — `^[a-z][-a-z0-9]{2,62}$`, — и расходилась с каноном в обе стороны: была УЖЕ него по началу имени (буква вместо буквы-или-цифры) и по длине (от 3 вместо 1), и ШИРЕ него по хвосту (допускала имя, кончающееся дефисом). Гейт единственности формы её не видел by construction: он ловит байт-идентичную копию канона, а независимо написанную регулярку — нет, и свою слепую зону называет сам.
Идентификатор роли остаётся своим и формой имени НЕ судится: `roles/vpc.admin` — то, на что ссылаются привязки, а не косметическая метка (записанное решение владельца, kacho#715). CustomRoleNameForm — форма имени ПОЛЬЗОВАТЕЛЬСКОЙ роли. Зеркало ограничения таблицы `roles_custom_name_check` (`0056_role_definition_tier.sql`, применённая миграция, ban #5).
const DescriptionMax = 256
DescriptionMax — предел описания в ЗНАКАХ, а не в байтах.
Единица — та же, что у ограничений базы (`*_description_check`: `length(description) <= 256`, Postgres считает знаки) и у загрузчика манифеста (`minRoleDescriptionRunes`). Счёт в байтах отвергал кириллическое описание в 129 знаков, которое база принимает: описание роли модуля, доставленное манифестом, проходило загрузку и роняло применение ролей на старте (kaname#856, роль `registry.public.read`).
const EditorDeleteNote = "co-materialized on in-scope leaf objects, NOT on the account/project anchor itself"
EditorDeleteNote is the verbatim explanation of the editor delete-qualifier.
const EditorDeleteVerb = "delete*"
EditorDeleteVerb is the qualifier appended to an editor-tier role's effective verbs (delete of in-scope leaf objects, not the anchor).
const ErrCredentialKindField = "credential_kind"
ErrCredentialKindField — имя поля, которое обязан называть отказ. Объявлено одним местом, чтобы тон отказа не разошёлся между двумя глаголами выдачи. #nosec G101 -- это ИМЯ ПОЛЯ запроса, которое обязан назвать текст отказа (конвенция отказов Kachō), а не значение удостоверения.
const HumanSessionIDPrefix = "hss"
HumanSessionIDPrefix — приставка идентификатора записи в дефисном каноне.
const LegacyPublicWildcardWord = "user:*"
LegacyPublicWildcardWord — прежнее слово публичного чтения в составе субъектов манифеста. Рендер блока переводит его в класс (`modelrender.subjectWords`), и поэтому оно — одно из написаний класса (IsClassSubjectWord).
const MaxAssertionIDLength = 256
MaxAssertionIDLength — потолок длины идентификатора однократности.
Значение выбирает ПРЕДЪЯВИТЕЛЬ, поэтому его длина — это его выбор нашего расхода. Потолок повторяет ограничение схемы: два места об одном предмете разошлись бы молча, поэтому проба сверяет их между собой.
const MaxSubjectsPerBinding = 32
MaxSubjectsPerBinding — hard upper bound on subjects[]. Anti-DoS + tractable per-subject audit/expand. 0 < n ≤ 32 enforced by NormalizeSubjects (sync, INVALID_ARGUMENT) and mirrored by the DB.
const MaxTargetResourcesPerBinding = 256
MaxTargetResourcesPerBinding — жёсткий потолок target.resources[]. Защита от отказа в обслуживании и посильная пообъектная материализация: реконсайлер пересекает каждый подошедший по правилам объект с целью через ЛИНЕЙНЫЙ AccessTarget.Contains внутри ОДНОЙ синхронной транзакции записи под рекомендательной блокировкой выдачи и повторяет это на каждый объект, созданный в кластере (forwardObjectForBinding). Неограниченный перечень стоил бы |подошедших|×|цели| сравнений на горячем пути и неограниченной строки JSONB. 0 < n ≤ 256 судится синхронно (INVALID_ARGUMENT), как MaxSubjectsPerBinding.
const OwnLaneSubjectHead = "own:"
OwnLaneSubjectHead — голова идентичности, отчеканенной нашей полосой.
const PKCEMethodS256 = "S256"
PKCEMethodS256 — ЕДИНСТВЕННЫЙ принимаемый метод испытания. `plain` не заводится: он сводит PKCE к передаче секрета в открытом виде, и колонка, допускающая его, была бы местом, куда он однажды ляжет.
const PersonalAccountNamePrefix = "personal-cloud-"
PersonalAccountNamePrefix — знак ПРОСТРАНСТВА ИМЁН личных аккаунтов (приёмка Ф4 Р9). Личный аккаунт заводит система как следствие регистрации (Р8): его имя `personal-cloud-<хвост>` выбирается без участия арендатора, и «personal-cloud-…» человек прочтёт, а идентификатор — нет.
Написание объявлено ЗДЕСЬ единожды и читается ДВУМЯ сторонами: генератором имени зеркала (`user.RegisterMirrorTx`) и резервом префикса в `AccountService.Create/Update`. Две копии разошлись бы молча — резерв перестал бы накрывать реально заводимые имена, а имя зеркала перестало бы попадать в зарезервированное пространство (code-authoring §5.1: одно написание значения).
const PrefixAccessKey = "ak"
PrefixAccessKey — префикс дефисной формы `ak-<17>`; сама константа канона — в фундаменте (`ids.PrefixAccessKeyHyphen`), здесь только имя для отказов и таблицы форм.
const PrefixGrantDelegation = "gdl"
PrefixGrantDelegation — префикс идентификатора делегирования (`gdl` + 17 знаков crockford-base32, `ids.NewID`).
const PublicReaderRelation = "public_reader"
PublicReaderRelation — имя отношения публичного чтения в блоке контейнера.
Объявлено ОДИН раз: его порождает генератор блоков модели, пишет разворачивание выдачи и принимает страж прямого факта (`relation_fact_subject_class_reads_only`), и второе написание разошлось бы с первым молча.
const PublicationRelation = "v_get"
PublicationRelation — отношение хранимой публикации.
const PublicationSubject = SpeakerAnonymous
PublicationSubject — ХРАНИМЫЙ субъект публикации объекта для чтения без входа (Р8, SC-12).
Входных слов у соседа два, перечень закрыт правилом приёма фундамента (`corelib/authz/proxytuple.PublicReadSubjects`, kaname#893): переходное `user:*` и `system_class:anonymous`, оба + `v_get`. Здесь — ЕДИНСТВЕННЫЙ субъект, которого хранит служба, какое бы слово ни пришло. Его зовут запись и снятие публикации, разбор остаточных фактов и миграция переезда: снятие, назвавшее другое написание, удалило бы несуществующий факт, и чтение анонимом устояло бы (SC-13).
const RecoveryCodeLength = 10
RecoveryCodeLength — длина кода в знаках алфавита без разделителя: 10 знаков по 5 бит — 50 бит случайности.
const SessionBearerBytes = 32
SessionBearerBytes — длина случайной части носителя в байтах: 32 байта = 256 бит, не меньше 128 требуемых (Ф3 §8 инв. 9). В URL-безопасном base64 без дополнения это 43 знака — не короче 22 требуемых (Ф3-08).
const ShortIDLen = 20
ShortIDLen — full id length (prefix + body); matches pkg/ids.
const SystemRoleNameForm = `^[a-z][-a-z0-9]*(\.[a-z][a-z0-9_]*){0,2}$`
SystemRoleNameForm — форма имени СИСТЕМНОЙ роли. Зеркало ограничения таблицы `roles_system_name_check` того же файла: нижний регистр, дефис в первом сегменте, подчёркивание в сегментах после первого, не более трёх сегментов.
Здесь стояло `^roles/[a-z]+\.[a-z]+$`, и этой форме не удовлетворяла НИ ОДНА живая строка: предикат `grep -c "'roles/"` по каталогу миграций даёт ноль во всех файлах, а имена продукта записаны как `vpc.network.admin`. То есть `Role.Validate()` отвергал каждую системную роль платформы — и покраснеть это не могло, потому что системную роль в Go до задачи kacho#1824 никто не строил.
Правило пережило свой предмет молча; сегодня оно зеркалит ограничение, которое действительно судит записываемую строку.
const TextHumanSessionIDRule = "must match ^hss-[crockford-base32]{17}$"
TextHumanSessionIDRule — правило формы идентификатора записи, как его называет отказ формы (Р5): `Illegal argument sessionId: <правило>`.
Variables ¶
var ( // ErrCeremonySessionUnknown — строки сессии, в которую заводится семейство, // НЕТ ВОВСЕ. // // Отдельно от «не жива», и это не педантизм: прежняя форма отвергала // отсутствующую сессию внешним ключом, называя его имя, — то есть // различение уже существовало, и слить его в отказ о живости значило бы // сделать опечатку в идентификаторе неотличимой от гонки с выходом. ErrCeremonySessionUnknown = errors.New("ceremony session: unknown") // ErrCeremonySessionNotLive — строка сессии ЕСТЬ, но сессия уже не жива: // снята выходом (своим либо принудительным) либо истекла. // // Состояний РОВНО ДВА, и оба сливаются намеренно: предъявителю они говорят // одно — «входа, в котором идёт церемония, больше нет». Третьего состояния // («сессии нет») здесь НЕТ — у него свой признак выше. // // Исход ЗАВЕДЕНИЯ, а не предъявления: разбирается не предъявленная строка, // а право завести новую. ErrCeremonySessionNotLive = errors.New("ceremony session: not live") // ErrCeremonySubjectCutOff — предъявленная строка (код либо токен // обновления) есть, активна и в сроке, но сессия, в которой она выдана, // аутентифицирована НЕ ПОЗЖЕ отсечки своего субъекта // (`user_token_revocations.revoke_before`). // // Исход ПРЕДЪЯВЛЕНИЯ, и у порта фундамента своего слова у него нет: это не // повтор — семейство по нему не отзывается, и журнал не называет атакой отзыв // доступа, — и не истечение. Порту он уходит «записи нет» // (`oauthceremony.ErrGrantNotFound`), причиной в цепочке: гранта, по которому // предъявитель пришёл, больше нет. ErrCeremonySubjectCutOff = errors.New("ceremony session: authenticated no later than its subject's cutoff") // ErrAccessTokenFamilyNotLive — выпуск токена доступа не записан: семейства, // в которое он заводится, нет либо оно отозвано (kaname#319). // // Исход ЗАВЕДЕНИЯ записи выпуска, а не предъявления. Два состояния сливаются // намеренно: выпуску говорят одно — «семейства, в котором идёт выдача, больше // нет», — и токен клиенту уезжать не должен. Различает их, если понадобится, // строка семейства, а не этот отказ. ErrAccessTokenFamilyNotLive = errors.New("access token: family is unknown or revoked") // ErrGrantOwnerUnverified — правило выдачи удостоверениям человека ответило // «владелец-человек не подтвердил адрес» (kaname#456, Р5б): пары нет, // предъявленный токен обновления не обёрнут (оборот и выдача — одна единица // работы), семейство не отозвано. Наружу — тот же тон `invalid_grant`, что // у всякого отказа после именования кода. ErrGrantOwnerUnverified = errors.New("ceremony grant: the owner has not verified the email address") // ErrGrantRuleUndecidable — правило выдачи спросить не смогли: отказ // операции (`temporarily_unavailable`), а не `invalid_grant` — клиент, // получивший `invalid_grant`, выбросил бы живой токен обновления. ErrGrantRuleUndecidable = errors.New("ceremony grant: the issuance rule could not be asked") // ErrVerifierAtCapacity — проверяющий секрета занят: все места ёмкости // (`passwordverify`) заняты другими проверками. Отказ ПОВТОРЯЕМЫЙ и наш, а не // «секрет неверен»: несостоявшаяся сверка вердиктом не становится. ErrVerifierAtCapacity = errors.New("secret verifier: at capacity") )
var ErrAccountNameOfTheIDForm = errors.New(
"Illegal argument name: the account id form is reserved for the account's own id")
ErrAccountNameOfTheIDForm — имя формы идентификатора аккаунта, не равное собственному идентификатору (приёмка «идентификатор аккаунта можно указать при создании», Р6).
Имя аккаунта уникально на весь кластер, а имя по умолчанию — сам идентификатор. Пока идентификатор может прислать вызывающий, имя формы идентификатора, взятое другим аккаунтом, выдавало бы занятость идентификатора любому, кто создаёт аккаунт, и заранее отнимало бы у будущего аккаунта его имя по умолчанию. С правилом владелец такого имени — только аккаунт с этим самым идентификатором. Тот же предикат стоит в схеме проверкой `accounts_name_is_not_a_foreign_id`; текст отказа у обоих один.
var ErrAssertionClientUnknown = errors.New("client assertion: client does not resolve in a registry able to assert")
ErrAssertionClientUnknown — идентификатор не резолвится ни в одну строку реестра, СПОСОБНОГО к утверждению.
Один признак на два состояния — «строки нет вовсе» и «строка есть у вида клиента, не располагающего ключевым материалом», — и это не упрощение, а требование: различимые исходы дали бы предъявителю ОРАКУЛ существования. Он сообщал бы, заведён ли такой клиент, а по нему устанавливают и то, каким именно видом он заведён.
var ErrAssertionReplayed = errors.New("client assertion: single-use identifier already redeemed")
ErrAssertionReplayed — утверждение с этим идентификатором однократности уже предъявлялось ЭТИМ клиентом.
Отдельный признак, а не общий отказ: наружу все отказы аутентификации неразличимы (приёмка F2 §7), но ВНУТРЬ различимость обязана существовать — иначе счётчик исхода не с чем связать, и мёртвый контроль становится невидимым.
var ErrBasicCredentialRefused = errors.New("credential refused")
ErrBasicCredentialRefused — ЕДИНСТВЕННЫЙ отказ полосы базового секрета.
Неизвестный идентификатор, неверный секрет, истёкший срок, отозванное удостоверение, неактивный владелец, вид, не принимаемый этой поверхностью, — ОДНА И ТА ЖЕ ошибка. Различимый исход есть ОРАКУЛ: по нему отличают «нет такого» от «есть, но не ваш», то есть ровно то, что скрытие и должно закрыть.
Различимость живёт ВНУТРЬ — в счётчиках и журнале по причинам, а не в тексте ошибки и не в ответе на проводе. Причина едет внутрь отказом-обёрткой (RefuseBasicCredential), чей текст дословно равен этому и для которого `errors.Is` с этим значением истинно. Отдельным исходом остаётся только НЕДОСТУПНОСТЬ АВТОРИТЕТА: это не отказ в удостоверении, а неспособность установить его состояние, и предлагать вызывающему переаутентифицироваться на неисправность, которую не исправит ни одно его удостоверение, значило бы вводить его в заблуждение.
var ErrEmpty = errors.New("required field is empty")
ErrEmpty — sentinel for empty required fields (used in NULL-validation before repo).
var ErrLoginVerifierNotSerializable = errors.New("login verifier is not serializable")
ErrLoginVerifierNotSerializable — материал не сериализуется. Отказ ГРОМКИЙ: молча выданное пустое читалось бы как «материала нет», то есть было бы ложью о строке.
var ErrRecoveryCodeNotSerializable = errors.New("recovery code is not serializable")
ErrRecoveryCodeNotSerializable — код не сериализуется ни в JSON, ни в текст: в письмо он уходит одним путём — `Letter` из писателя очереди.
var ErrScopeMismatch = errors.New("scope does not match resource_type / resource_id")
ErrScopeMismatch — Scope does not match (resource_type, resource_id). Service-layer maps to gRPC InvalidArgument.
var ErrSessionBearerNotSerializable = fmt.Errorf("session bearer is not serializable")
ErrSessionBearerNotSerializable — носитель не сериализуется ни в JSON, ни в текст: значение уходит клиенту одним путём — заголовком `Set-Cookie`, который пишет обработчик полосы, — и попасть в журнал либо в ответ иным путём не может.
var ErrTrustedIssuerUnknown = errors.New(
"federated assertion: (issuer, subject) does not resolve in the trusted-issuer list")
ErrTrustedIssuerUnknown — пара (издатель, субъект) не резолвится ни в одну запись перечня.
ОДИН признак на все состояния «мы за это не ручаемся»: записи нет вовсе · доверие выдано другому субъекту того же издателя · доверие выдано, но строка, которую оно уполномочивало, снята. Различимые исходы дали бы предъявителю оракул: по ним устанавливают, заведено ли доверие и кому именно.
var LoginMethodLookupSecret = LoginMethodKind(assurance.MethodLookupSecret.String())
LoginMethodLookupSecret — набор запасных кодов: строка несёт проверочный материал набора, из которого значение кода не восстановимо (Ф12 Р6).
var LoginMethodPassword = LoginMethodKind(assurance.MethodPassword.String())
LoginMethodPassword — пароль: строка несёт его проверочный материал.
var LoginMethodTOTP = LoginMethodKind(assurance.MethodTOTP.String())
LoginMethodTOTP — одноразовый код по времени (RFC 6238): строка несёт секрет ОБЁРНУТЫМ (Ф12 Р2) и последний принятый шаг.
Functions ¶
func AccountScopeApplies ¶
AccountScopeApplies отвечает, применима ли область аккаунта к виду.
func AllMaterializableTypes ¶
func AllMaterializableTypes() []string
AllMaterializableTypes returns the closed, sorted, deduped set of `<module>.<resource>` types the reconciler can materialize per-object. It is the wildcard-expansion set for a BOUNDED-scope `*.*` rule (issue kacho-iam#224 / D-8a): an owner role bound at ACCOUNT/PROJECT becomes an explicit per-object admin on EVERY object kind inside the scope, instead of relying on the FGA derivation cascade.
This is a STRICT SUPERSET of labelSelectableTypes, differing by exactly registry.repositories (materializable, not label-selectable). Every other materializable type (mirror-fed vpc/compute/loadbalancer + EVERY iam-native type) is also ARM_LABELS-selectable. The iam content types (user/serviceAccount/group/ role/accessBinding) remain materializable by ARM_ANCHOR/ARM_NAMES (replacing the flat model's missing `from account` cascade with per-object materialization) AND are additionally label-selectable.
Sorted so the resulting selector + role_rule_selectors index are deterministic (stable fast-path JOIN + migration-seed lockstep, rule_wildcard_scope_test.go).
func BindableScopes ¶
func BindableScopes() []string
BindableScopes — копия закрытого набора областей выдачи, годных в предки. Копия, а не сам срез: набор закрыт, и вызывающий не вправе его пополнить.
func CeremonyFamilyBound ¶ added in v0.5.0
CeremonyFamilyBound — граница семейства собственной церемонии: меньшее из срока сессии, в которой семейство выдано, и рождения семейства плюс срок семейства, названный установкой (`authn.ceremony.refresh-ttl`, kaname#318). Семейство кончается вместе со своим входом и не живёт дольше своего срока: оборот выпускает преемника, но границы не сдвигает.
Правило названо ЗДЕСЬ один раз: его спрашивают и выдача кода (рождение — момент выдачи), и сборка гранта из записи семейства (рождение — отметка строки). Сессия без срока (нулевой момент) границы не сужает. Потолок срока семейства судит страж старта; здесь срок — уже принятая величина.
func CeremonyScopes ¶ added in v0.5.0
func CeremonyScopes() []string
CeremonyScopes — ЗАКРЫТЫЙ перечень областей, которые интерактивный клиент вправе запросить у точки авторизации. Копией: вызывающий не расширит его на месте. Область вне перечня отвергается движком церемонии (`invalid_scope`) — принять её и ничего по ней не сделать было бы «принято и проигнорировано».
func ClassNestedRuleRefusal ¶ added in v0.5.0
func ClassNestedRuleRefusal(role RoleID, rules Rules, containers map[string]PublicReadContainer) (string, bool)
ClassNestedRuleRefusal — ворота выдачи СИСТЕМНОМУ КЛАССУ роли с вложенными правилами (Р5, ND-05). Судится КАЖДОЕ вложенное правило роли:
- контейнер объявлен (`publicReadToChildren`), иначе — текст SC-65 соседа;
- тип вложенного правила — ПРЯМОЙ ребёнок контейнера, иначе — до внука `public_reader` не доходит;
- правило даёт ровно `get` и `list` и не сужено.
Первое невыполненное условие — отказ с его текстом. Роль без вложенных воротам безразлична. `containers` — что живой каталог говорит о типах- владельцах; тип, о котором не сказано, читается необъявленным.
func CustomDefinitionTierToScope ¶
CustomDefinitionTierToScope maps a wire definition_tier (dotted tierType + anchor id) to the account/project scope of a CUSTOM role (redesign-2026 F4). Pre-Phase-0 tierType is REQUIRED (prefix-derivation is B3-gated, so an empty tierType is rejected). iam.cluster is rejected — system roles are seeded by migration, never created via the public API. ok=false for an empty / iam.cluster / unknown tierType (the caller turns it into INVALID_ARGUMENT "Illegal argument definitionTier").
func CutoffBelowFirstAuthentication ¶ added in v0.5.0
CutoffBelowFirstAuthentication — момент отсечки, которой глагол снимает носители, выданные ДО первой аутентификации личности нашей посадкой, и не задевает ни одной сессии нашей посадки: на единицу разрешения хранилища (микросекунда) раньше первой аутентификации (Ф3 Р4, Р5, Ф1 §4.2). Край сравнивает отсечку с моментом сессии включающе, поэтому равенство с первой аутентификацией сняло бы самую раннюю сессию. Правило одно для выхода, смены пароля и снятия ключа доступа (Ф13 Р8): второе написание разошлось бы молча.
func DerivedIDSuffix ¶
DerivedIDSuffix возвращает первые семнадцать шестнадцатеричных символов `md5(seed)` — дословно то же, что постгресовое `substr(md5(seed), 1, 17)`.
Все шестнадцатеричные символы годны как крокфордова база-32 (`0-9a-f` ⊂ `[0-9a-hjkmnp-tv-z]`), поэтому производные идентификаторы проходят ограничения формы `soc_`/`cag_`.
Ничего секретного здесь нет: вход — посевные строки, выход — публичные идентификаторы. Дайджест зафиксирован уже применённой миграцией, поэтому иной дайджест был бы не «крепче», а просто перестал бы адресовать существующие строки.
func EncodeRules ¶
EncodeRules encodes domain Rules to the roles.rules JSONB payload shape.
func FGASubjectRef ¶
FGASubjectRef formats the FGA "user" side of a tuple for a subject:
user → user:<id> service_account → service_account:<id> group → group:<id>#member (computed relation expands group members) system_class → system_class:<anonymous|authenticated>
Empty / unknown subject_type defaults to user (defensive). SINGLE source of truth shared by the AccessBinding grant-tuple builder (tuples.go / scope_grant_tuples.go) and the ARM_LABELS reconciler (reconcile) — the two MUST stay byte-symmetric, else a member's grant and revoke would target different FGA users and leak standing access.
func HasSystemClass ¶ added in v0.5.0
HasSystemClass — среди субъектов выдачи есть системный класс.
func HumanSessionEndReasons ¶ added in v0.5.0
func HumanSessionEndReasons() []string
HumanSessionEndReasons — перечень ЗАКРЫТОГО словаря причин снятия записи сессии: ровно те значения, что принимает `human_sessions_ended_reason_check`. Причины отсечки RevokeReasonSecondFactorReset и RevokeReasonAccessKeysReset в него не входят — они не снимают ни одной записи.
Функция, а не переменная: каждый вызов отдаёт СВЕЖИЙ срез, и вызывающий, дописавший в него, словаря не меняет. Элементы — константы по имени, а не литералы: слово объявлено один раз. Совпадение перечня с ограничением базы в обе стороны держит проба KN-SER-07 (`internal/migrations/human_session_forced_exit_reason_integration_test.go`).
func IsAssertionClientUnknown ¶
IsAssertionClientUnknown отвечает, не резолвится ли клиент.
func IsAssertionReplayed ¶
IsAssertionReplayed отвечает, отвергнуто ли предъявление как ПОВТОР.
Предикат, а не сравнение на месте: вызывающие обёртывают ошибку контекстом, и сравнение `err == ErrAssertionReplayed` перестало бы работать на первом же обёртывании — молча, потому что «не повтор» и «не смогли распознать» выглядят одинаково.
func IsClassReadVerb ¶ added in v0.5.0
IsClassReadVerb — глагол чтения, допустимый у выдачи классу (Р4, SC-23).
func IsClassSubjectWord ¶ added in v0.6.0
IsClassSubjectWord — слово состава субъектов называет системный класс: `system_class`, `system_class:<класс>` либо прежнее слово LegacyPublicWildcardWord, которое рендер переводит в класс.
Единственное написание правила (kaname#866, CE-03): его читают загрузчик манифеста (Р2 — класс в действии чтения ресурса с ключом) и допуск собранной модели (Д3 — запись класса в блоке нового типа). Узнаётся по ВИДУ слова, а не по имени класса: имена — закрытый словарь, и запись с незнакомым именем обязана дойти до отказа, а не проскочить мимо него.
func IsIdentityCarriedKind ¶ added in v0.3.0
IsIdentityCarriedKind — вид считается в личности.
func IsLabelSelectableType ¶
IsLabelSelectableType reports whether a `<module>.<resource>` type may carry a match_labels (ARM_LABELS) selector (feed-gate). Unified model: every iam-native type is label-selectable (project/account + the content types user/serviceAccount/group/role/accessBinding). It consults ONLY labelSelectableTypes — registry.repositories is materializable but NOT label-selectable, so it stays out of this predicate.
func IsPersonalAccountName ¶ added in v0.5.0
func IsPersonalAccountName(name AccountName) bool
IsPersonalAccountName — имя принадлежит зарезервированному пространству личных аккаунтов. Дефис на границе несущий: `personal-cloud-abc` зарезервировано, а `personal-cloudabc` (без дефиса) — обычное имя ВНЕ резерва.
func IsPostureStatedKind ¶
IsPostureStatedKind — величину этого вида объявляет посадка, а не авторитет.
func IsPublicReadVerb ¶ added in v0.6.0
IsPublicReadVerb — действие с этим именем касается публичное чтение ([publicReadVerbsOfChild]). Имя сравнивается в нормальной форме (NormalizeVerb).
func IsRetiredType ¶
IsRetiredType reports whether a dotted `<module>.<resource>` names a resource the platform has retired. A retired type is not grantable on any rule arm.
func IsRoleAssignable ¶
IsRoleAssignable reports whether role r may be bound on resource (resourceType, resourceID) per the STRICT assignability matrix.
func IsRoleAssignableInAccount ¶
IsRoleAssignableInAccount extends IsRoleAssignable with the hierarchy-down rule that needs the scope's RESOLVED owning-account — knowledge the stateless predicate does not have (acceptance IAM-1-25). scopeOwningAccountID is the account that OWNS the scope anchor: for an account scope it is the account id itself; for a project scope it is the project's account_id (resolved by the caller via a project→account lookup); for cluster / cross-service scopes it is "".
It admits the strict matrix (IsRoleAssignable) PLUS the single hierarchy-down case: an iam.account-tier custom role is assignable on a PROJECT nested in the role's own account (role.account_id == the project's owning account). The account boundary is never crossed — an account-role of a DIFFERENT account stays not-assignable — and no other tier gains breadth (system stays everywhere, project-role stays own-project). scopeOwningAccountID=="" (unresolved / non-account scope) collapses to the strict predicate, so a missing resolve never over-grants (fail-closed).
func IsScopeAnchorKind ¶
IsScopeAnchorKind reports whether a bare within-service kind may anchor an AccessBinding. Only the three hierarchy tiers can; a per-object type names an object UNDER the anchor and belongs to the `target` axis (F8), whose vocabulary is the live platform catalog (`ValidTargetType`, kaname#904).
func IsSeededResourceID ¶
IsSeededResourceID отвечает, назван ли литерал закрытым перечнем посеянного.
Проверку ПРЕФИКСА он на себя НЕ берёт: её делает вызывающий (`shared.ValidateResourceID`), поэтому посеянный id роли не проходит там, где ждут аккаунт.
func IsStoredPublication ¶ added in v0.5.0
IsStoredPublication — хранимый факт есть публикация для чтения без входа. Узнаётся по идентичности хранимой пары, а не по входному слову: после переезда переходное слово (`user:*`) в фактах не встречается, а слово-класс совпадает с хранимым лишь написанием, не происхождением.
func IsSystemClassName ¶ added in v0.5.0
IsSystemClassName — имя из закрытого словаря классов.
func IsTrustedIssuerUnknown ¶
IsTrustedIssuerUnknown отвечает, не резолвится ли пара.
Предикат, а не сравнение на месте: вызывающие обёртывают ошибку контекстом, и сравнение на равенство перестало бы работать на первом же обёртывании — молча, потому что «не доверенный» и «не смогли распознать» выглядят одинаково, а второе означало бы недоступность реестра, у которой исход СВОЙ.
func IsVerbOfType ¶
IsVerbOfType сообщает, объявляет ли ТИП этот глагол, то есть материализуется ли он как отношение `v_<глагол>` НА ЭТОМ типе.
Заменила снятый глобальный словарь глаголов на путях эмиссии. Разница не косметическая: глобальный словарь отвечал одинаково для всех типов, поэтому «у этого типа такого глагола нет» было невыразимо — и правило, называющее соседний глагол, порождало кортеж с отношением, которого у типа не существует. Пустой набор не принадлежит никому (fail-closed): тип, не объявивший ничего, не получает ни одного `v_*`.
func IsWellFormedModuleName ¶
IsWellFormedModuleName — годно ли имя по ФОРМЕ, безотносительно членства.
Читателей у грамматики ДВА, и они судят разные поля: сегмент `module` правила роли (validateModule) и оболочку манифеста (`module:`). Вторая копия образца разошлась бы с первой молча — на том имени, о котором знает только одна.
Подстановочный знак `*` формой имени НЕ является: он маркер политики, и разбирается Rule.Validate отдельно.
func IsWellFormedObjectTypeName ¶
IsWellFormedObjectTypeName — годно ли имя типа объекта по ФОРМЕ, безотносительно того, объявлял ли его кто-нибудь.
func ModuleNameGrammar ¶
func ModuleNameGrammar() string
ModuleNameGrammar — образец имени модуля СТРОКОЙ, для текстов отказа.
Отказ обязан назвать не только негодный токен, но и правило: без правила автор манифеста узнаёт, что ошибся, и не узнаёт, чем именно. Величина берётся у того же объявления, что судит, — выписанная рядом, она разошлась бы с ним молча.
func ModuleOfCatalogType ¶ added in v0.7.0
ModuleOfCatalogType — модуль точечного типа каталога по его имени (`<модуль>.<ресурс>`, например `registry.registries` → `registry`). Пусто, если имя без точки.
func NestedRuleBeyondDirectChildText ¶ added in v0.5.0
NestedRuleBeyondDirectChildText — вложенное правило называет не ПРЯМОГО ребёнка контейнера: `public_reader from <указатель>` до внука не доходит.
func NestedRuleNotWholeText ¶ added in v0.5.0
Тексты отказа ворот класса (Р5, ND-05) — контракт тона, дословно приёмочные.
NestedRuleNotWholeText — вложенное правило не читает тип-ребёнок целиком: даёт не ровно `get` и `list` либо сужено именами или метками.
func NewKac127ID ¶
NewKac127ID возвращает идентификатор формата `<prefix>_<17-char crockford>`. Panic если prefix пустой (programmer error: префикс приходит из package-level константы).
func NormalizeVerb ¶
NormalizeVerb — ЕДИНСТВЕННАЯ точка приведения имени глагола к канонической форме.
Разрыв, который она закрывает, был двусторонним. Проверка принадлежности приводила ВХОД, а индекс словаря строился ДОСЛОВНО — словарная запись с заглавной буквой не нашлась бы никогда. И наоборот: имя отношения собиралось из АВТОРСКОГО написания, поэтому написание, отличающееся регистром, проходило проверку и адресовало отношение, которого в модели нет; владелец модели отвергает такую запись окончательно, а отказ считается постоянным — строка навсегда блокирует свою партицию очереди.
Приведение ТОЖДЕСТВЕННО на всех существующих глаголах, поэтому ни одно отношение, ни один кортеж и ни одна запись каталога не меняются.
func ObjectTypeNameGrammar ¶
func ObjectTypeNameGrammar() string
ObjectTypeNameGrammar — образец имени типа СТРОКОЙ, для текстов отказа и для сверки с ограничением базы.
Отказ обязан назвать не только негодный токен, но и правило: без правила автор манифеста узнаёт, что ошибся, и не узнаёт, чем именно. Величина берётся у того же объявления, что судит, — выписанная рядом, она разошлась бы с ним молча.
func OrderVerbsForDisplay ¶
OrderVerbsForDisplay возвращает глаголы в КАНОНИЧЕСКОМ порядке показа: сперва старшинство (get/list/create/update/delete), затем всё остальное — стабильно, по алфавиту.
Точка порядка ОДНА на всё превью и на публичное поле каталога. Порядок этих поверхностей — часть контракта: его читают существующие клиенты. Пока он жил внутри превью, поле каталога брало порядок из своего источника — и смена источника на пересечение наборов молча переставила бы значения по алфавиту.
func PasswordHashFormatMarkers ¶ added in v0.5.0
func PasswordHashFormatMarkers() []string
PasswordHashFormatMarkers — признаки перечня, отсортированные: перепись и тексты отказов печатают их в устойчивом порядке.
func RefuseBasicCredential ¶ added in v0.5.0
func RefuseBasicCredential(reason BasicCredentialRefusalReason) error
RefuseBasicCredential — единый отказ полосы с названной причиной.
func ResolveVerbsAndTier ¶
ResolveVerbsAndTier expands a rule's authored verbs (verb `*` → набор ТИПА) and derives the per-RULE back-compat tier (strongest verb-class among the rule's verbs), mapped the SAME way the consumer authz-gate resolves an action: get/list → viewer ; create/update (+ domain mutations) → editor ; delete → admin. Per-RULE, never whole-role (B-11). The tier tuple keeps tier-based Check call-sites working; the v_* tuples carry the precise per-verb enforcement. typeVerbs — набор глаголов, объявленный ТИПОМ, на который правило адресовано. Он приходит ПАРАМЕТРОМ от вызывающего, который тип уже знает: владельцем таблицы остаётся authzmap, а домен — без внешних зависимостей (см. объявление файла). Вызывающий, у которого типа нет (правило не резолвится ни в один известный), передаёт словарь, общий для всех ресурсов, — это его решение, не домена.
func RetiredTypes ¶
func RetiredTypes() []string
RetiredTypes returns the closed, sorted set of retired dotted types. Exported so the gates that keep the retirement complete can be driven from ONE list rather than from a copy of it.
func RoleBoundToSystemClassText ¶ added in v0.5.0
RoleBoundToSystemClassText — текст отказа правке роли, выданной классу (SC-22). Один производитель на синхронный отказ и на отказ записи.
func RoleDeclaringModule ¶ added in v0.7.0
RoleDeclaringModule — модуль, манифест которого объявил роль; пусто — роль не модульная (платформенная, своя роль аккаунта либо проекта). Это и есть «роль модуля» Д2 приёмки.
Владельца несёт колонка `OwnerModule`, её проставляет применитель манифеста. У ролей, посеянных до её заведения, колонка пуста, и владение читается ТЕМ ЖЕ признаком, что у снятия ролей модуля (`RetireRole`): первый сегмент имени кластерной роли. Признак принимается лишь тогда, когда каждое правило роли называет этот же модуль: платформенные `admin`, `edit`, `view`, `owner` точки в имени не несут, а у `system.*` модуль правил с сегментом имени не совпадает.
func RoleNotReadOnlyForClassText ¶ added in v0.5.0
RoleNotReadOnlyForClassText — текст отказа выдаче классу роли с пишущим глаголом (SC-21). Один производитель на синхронный отказ и на отказ записи.
func RuleRefsByRule ¶
func RuleRefsByRule(rules Rules) [][]RoleRuleRef
RuleRefsByRule — объявленные сегменты, разложенные ПО ПРАВИЛАМ.
Длина результата равна длине входа ВСЕГДА: правило без адресуемых сегментов даёт пустой срез, а не пропускается. Иначе индекс перестал бы указывать на своё правило — то есть ключ RuleState.RuleIndex стал бы ложным.
Чем это отличается от [RuleRefsOf], и различие несущее ¶
RuleRefsOf дедуплицирует сегменты ПО ВСЕЙ РОЛИ: два правила, объявившие один сегмент, дают одну строку — и это верно для вопроса «сколько адресуемых сегментов у роли». Здесь наоборот: дедупликация только ВНУТРИ правила, потому что сегмент, потерянный обоими правилами, потерян у ОБОИХ, и схлопнув их, мы объявили бы одно из них действующим.
func ScopeTierByKind ¶
ScopeTierByKind — копия словаря «вид → ярус».
Экспорт заведён ради ОДНОГО вызывающего — пробы согласия с триггером схемы (см. §«Третье место» выше). Второе объявление отношения живёт в другом пакете, поэтому сверить его иначе нечем: без этого читателя согласие двух объявлений было бы обещанием, а не проверкой.
Отдаётся копия: словарь неизменяем by construction, и вызывающий не должен иметь возможности это нарушить.
func ScopeTypeFromDotted ¶
ScopeTypeFromDotted maps the dotted wire scopeType to the bare within-service anchor kind. ok=false for any value outside the closed three-tier set (empty, non-dotted bare, or unknown dotted) — the caller rejects it with InvalidArgument.
func ScopeTypeToDotted ¶
ScopeTypeToDotted maps the bare within-service anchor kind to the dotted wire scopeType. An unrecognized kind is returned unchanged (defensive — a binding anchor is always one of the three tiers).
func SeedIdentityDeclared ¶ added in v0.3.0
SeedIdentityDeclared приводит написание к ОБЪЯВЛЕННОМУ: имя из окна отдаётся своей парой, имя вне окна — собой.
Нужен там, где имя есть КЛЮЧ ДИСПЕТЧЕРИЗАЦИИ, а не значение запроса: разбор ветвится по одному написанию, и приводить к нему обязан один помощник, а не каждая ветвь по-своему.
func SeedIdentitySpellings ¶ added in v0.3.0
SeedIdentitySpellings — ВСЕ написания, которыми сегодня адресуется тот же объект: само присланное имя плюс его пара по окну, если она есть.
Отношение симметрично: присланное прежнее написание резолвится в объявленное и наоборот. Асимметричное окно приняло бы манифест чужого продукта и отвергло свой собственный, переведённый, — а перевод идёт своим порядком (П3).
Имя вне окна возвращается ОДНИМ элементом, а не пустым срезом: окно расширяет приём ровно на объявленные пары и ничего не решает про остальные имена.
func SeededResourceIDs ¶
func SeededResourceIDs() []string
SeededResourceIDs возвращает перечень для гейта предмета. Копия — чтобы вызывающий не мог расширить приём, правя чужую карту.
func SpeakerClasses ¶ added in v0.5.0
SpeakerClasses — классы, которые получает вопрос от имени `speaker`, СВЕРХ самого говорящего (Р2).
Правило перечисляет виды, которым классы положены, а ветви «все прочие» у него нет:
- человек и сервисная учётка (`user:<id>`, `service_account:<id>`) — вошедший: оба класса. Вход не отнимает прав, поэтому выданное анониму читает и вошедший;
- `system_class:anonymous` — вызывающий без личности: классов сверх себя у него нет. Обратного вывода нет никогда: у анонима нет `authenticated` (SC-10);
- группа (`group:<id>`, `group:<id>#member`), подстановка любого вида (`user:*`, SC-15) и всё прочее — ничего: группа говорящим-личностью не является (SC-11), подстановка говорящим не бывает вовсе. Вопрос о вызывающем без личности задаётся классом `system_class:anonymous`, а не подстановкой: плоскость данных реестра kacho переведена на класс (SC-39), и переходного отображения подстановки в аноним нет.
Безымянность — значение входа (`system_class:anonymous`), а не вывод из формы строки: пустой и нераспознанный говорящий отвергаются раньше, разбором SpeakerRefusal.
func SpeakerRefusal ¶ added in v0.5.0
SpeakerRefusal — отказ говорящему, который говорящим не бывает (Р3).
Один разборщик на все методы, принимающие говорящего (`Check`, `BatchCheck`, `CheckRelation`): отказ в одном из них оставил бы соседний спрашивающим от имени «вошедшего без личности».
- `system_class:authenticated` — «вошедший» без названной личности есть противоречие (SC-14); любое имя класса, кроме `anonymous`, — тем же отказом;
- подстановка любого вида, `user:*` в том числе, — не говорящий (SC-15): она называет множество, а не того, кто спрашивает.
nil — говорящий допустим.
func UndeclaredContainerText ¶ added in v0.5.0
UndeclaredContainerText — текст отказа выдачи классу на объект типа, не объявленного контейнером, ролью, правило которой покрывает его тип-ребёнок (SC-65). Контракт тона — дословно приёмочный.
func ValidKeyIDForm ¶
ValidKeyIDForm — форма идентификатора ключа как предикат для приёмной стороны. Отдельная функция, а не экспортированная регулярка: регулярку вызывающий скопировал бы, и две копии разошлись бы молча.
func ValidTargetType ¶
func ValidTargetType(cat TargetTypeCatalog, dotted string) bool
ValidTargetType сообщает, может ли точечный `<module>.<resource>` быть назван типом пообъектной цели выдачи: тогда и только тогда, когда у типа есть ЖИВАЯ строка каталога платформы (Р1 приёмки `access-target-names-live-catalog-types.md`, kaname#904).
Почему источник — каталог. Цель чтится в одном месте: реконсайлер сравнивает её с типом объекта из зеркала точным совпадением строк (`AccessTarget.Contains`), а зеркало принимает объект лишь живого типа (`resource_mirror/emitter.go`, отказ «is not a live entry of the platform resource catalog»). Множество типов, которые лента МОЖЕТ произвести, и есть живой каталог — включая типы, заведённые манифестами модулей. Закрытый перечень в коде (`materializableTypes`) отвечал на этот вопрос второй, отдельно сопровождаемой копией и ошибался в обе стороны: тип модуля, живой в каталоге, отвергался — и выдача на объект модуля выражалась только всей областью, то есть ШИРЕ запрошенного; а тип, которого лента не производит, принимался бы и не давал ничего.
Подстановочный `*`, не-точечное имя, другое написание и снятая строка дают false: ни у одного из них живой строки нет. Пустой каталог (nil) — false на всё: отсутствие источника не превращается во всеразрешение.
func ValidateAssertionID ¶
ValidateAssertionID проверяет форму идентификатора однократности ДО того, как он попадёт в хранилище.
Отказ здесь наступает раньше обращения к базе — иначе потолок держало бы ограничение схемы, а вызывающий получал бы отказ хранилища вместо отказа формы, и счётчик исхода двигался бы не тот.
func ValidateCeremonyDigest ¶ added in v0.5.0
ValidateCeremonyDigest — форма свёртки кода либо токена.
func ValidateCeremonyLevel ¶ added in v0.5.0
ValidateCeremonyLevel — уровень аутентификации гранта из той же закрытой оси, что у сессии (`assuranceLevelValues`, Ф11): семейство несёт СНИМОК уровня сессии на выдаче кода (миграция `20260925121413`), и второго словаря у этого предмета нет.
func ValidatePKCEChallenge ¶ added in v0.5.0
ValidatePKCEChallenge — форма испытания и метода.
func ValidateRedirectURIs ¶
ValidateRedirectURIs — the redirect rule, exported because both the Create and the Update path must apply exactly the same one (api-conventions: a mutable field is validated on Update by the same rules as on Create).
WHY https IS NOT COSMETIC. A redirect target is where the authorization code — a credential — is delivered. A plaintext target hands it to anyone on the path; a relative or opaque one lets the provider's own matching rules decide where it lands. Both are rejected here rather than left to the provider, because the provider's answer arrives too late to be a contract: the caller would see a peer error instead of a named field.
Types ¶
type AccessBinding ¶
type AccessBinding struct {
ID AccessBindingID
SubjectType SubjectType
SubjectID SubjectID
RoleID RoleID
ResourceType ResourceType
ResourceID string // opaque id (any prefix, cross-service OK)
Scope Scope // RBAC v2 — anchor tier (CLUSTER/ACCOUNT/PROJECT)
Status AccessBindingStatus // PENDING|ACTIVE|REVOKED
ExpiresAt *time.Time // nullable — TTL
GrantedByUserID UserID // audit
RevokedAt *time.Time // nullable
RevokedByUserID *UserID // nullable
CreatedAt time.Time
// MaterializedAt — OUTPUT-ONLY. When this binding's per-object access last
// became live: MAX(updated_at) over its ACTIVE rows in
// access_binding_target_members (the reconciler's ledger). Zero when nothing
// has materialized yet.
//
// It exists because Kachō is eventually-consistent by contract: Operation.done
// means the binding row is DURABLE, never that the FGA tuples are visible
// (gating done on downstream visibility is forbidden — ban #9). In that window
// the grantee's 403 is byte-identical to a real denial, leaving the granting
// admin with nothing to distinguish "propagating" from "wrong grant" and
// nothing to poll. This field is the observable — NOT a barrier: it is filled
// by the read path from an already-written ledger and never blocks a write.
//
// Legitimately zero forever for a binding that materializes no per-object
// membership (CLUSTER scope — served by the flat short-circuit; a legacy
// permissions-only role with no authored rules[]). Zero therefore means "no
// ACTIVE per-object member", NOT "the grant is broken".
//
// Not persisted on the access_bindings row; filled by the read-side batch
// projection, like Subjects.
MaterializedAt time.Time
// DeletionProtection guards the binding from Delete (RBAC explicit-model 2026
// P6 — D-10, by the image of vpc.address.deletion_protection). The owner
// auto-binding created on Account.Create sets it true; Delete on a protected
// binding → FAILED_PRECONDITION (sync pre-check + atomic CAS backstop). Cleared
// via Update(update_mask=["deletion_protection"]) (C-03). Default false.
DeletionProtection bool
// GrantedRelation — ВТОРАЯ ФОРМА выдачи: имя отношения модели, выдаваемое
// напрямую на области выдачи, взаимоисключающе с RoleID (ограничение БД
// `access_bindings_grant_form_ck`). Роль раздаёт ГЛАГОЛЫ через свои правила, а
// встроенные права платформы выражены именованными отношениями
// (`system_viewer`, `fga_writer`, `viewer` на кластере) — подобрать роль под
// каждое означало бы завести роли с пустыми правилами.
//
// Непусто ТОЛЬКО у системной выдачи: отношение — внутреннее имя, от которого
// зависит решение о доступе, и выдавать его вправе только платформа. На вход
// создания это поле не принимается вовсе — его нет в запросе, поэтому «принято
// и проигнорировано» здесь невозможно by construction.
GrantedRelation string
// System — выдача заведена платформой (встроенный доступ: права служебных
// учёток, публичное чтение справочников). OUTPUT-ONLY на публичном контракте.
//
// Отвечает на вопрос «кто выдал» точнее, чем подставленная учётка: выдал не
// человек, а платформа, поэтому GrantedByUserID у такой выдачи пуст, и это не
// пробел аудита, а его содержание.
System bool
// Subjects — the full multi-subject set (RBAC rules-model 2026).
// Persisted in the access_binding_subjects child table; SubjectType/
// SubjectID above remain the legacy single = Subjects[0] (projection +
// the active-grant UNIQUE anchor). Empty on a row read without the child
// load — read-side Get/List fills it. On Create the use-case
// NormalizeSubjects-resolves it from the request before persisting.
Subjects []Subject
// Labels — tenant-facing метки САМОГО ресурса AccessBinding. Делают
// AccessBinding label-selectable наравне с account/project (ARM_LABELS-грант
// на iam.accessBinding → v_list по `labels @> matchLabels`; List фильтрует
// viewer ∪ v_list).
Labels Labels
// Target — WHICH objects under the scope-anchor the grant applies to
// (redesign-2026 F8). AllInScope = whole anchor (incl. future); Resources =
// closed per-object set. The zero value is treated as AllInScope (legacy /
// internal rows); the public Create RPC rejects a missing target (least-priv).
Target AccessTarget
// RevocationReason — почему выдача отозвана (kaname#963, Д2). Пусто у
// действующей выдачи и у отозванной до заведения поля (Д7).
RevocationReason RevocationReason
// DelegationID — делегирование права выдавать, по которому выдачу сделал
// делегат (kaname#986, Р2). Пусто у выдачи держателя права выдавать. Ставит
// только делегированный путь права выдавать; на вход запроса поле не
// принимается.
DelegationID GrantDelegationID
// HeldTargets — НЕ ХРАНИТСЯ. Объекты цели, которые на приёме выдачи были
// зарегистрированы (Р8 приёмки `unregistration-revokes-its-grants.md`). Запись
// выдачи берёт на каждом замок строки регистрации и отказывает, если строки
// уже нет: выдача, принятая до снятия и записанная после, живой не остаётся
// (URG-24). Пусто — судить нечего (ключи свободны либо цель — вся область).
HeldTargets []ResourceRef
}
AccessBinding — link (subject_type, subject_id) ↔ role_id ↔ (resource_type, resource_id) with lifecycle fields (status, expires_at, granted_by, revoked_at/revoked_by).
State machine: PENDING → ACTIVE → REVOKED (terminal). Transitions are atomic CAS-style UPDATEs (no TOCTOU). REVOKED is irreversible via `WHERE status IN ('PENDING','ACTIVE')`.
DB partial UNIQUE access_bindings_active_grant_uniq (migration 0003; WHERE revoked_at IS NULL) → strict INSERT: a duplicate active grant raises ErrAlreadyExists with verbatim text «these permissions are already granted to <subject_id> on <res_type>:<res_id>». Re-grant after revoke is allowed (revoked rows are out of the partial UNIQUE scope).
func (AccessBinding) StructuralParent ¶
func (b AccessBinding) StructuralParent() (StructuralTuple, bool)
StructuralParent returns the binding's scope parent-pointer:
project:<resourceID> → project → iam_access_binding:<id> account:<resourceID> → account → iam_access_binding:<id> cluster:<resourceID> → cluster → iam_access_binding:<id>
This triple is what `iam_access_binding.super_admin: super_admin from project or admin from account or any_admin from cluster` resolves over — the sole enabler of the cascade on a binding. ok=false when the scope is not a hierarchy parent, the binding has no id, or the scope has no id.
The empty-scope-id case is unreachable through the API — validateScopeID pins the cluster tier to the singleton id and requires a non-empty account/project id — and the guard is here for what it prevents rather than for what it expects: without it such a row projects `cluster:` with an empty object id, which the store rejects, and a rejected write in the outbox is retried rather than dropped, so one malformed row would hold up its partition indefinitely.
It is deliberately INDEPENDENT of status. A revoked binding keeps its row, and it is still a record that has to be readable and deletable by the administrator of its account; a status filter here would take that away precisely when the queue is behind. It cannot bring the grant back: what the grantee held were tuples on the SCOPE object, and this is a pointer at the binding, not a grant on the scope. Locked by service/cascade_queue_independence_integration_test.go (TestRevokedBindingStaysManageableAndGrantsNothing).
func (AccessBinding) Validate ¶
func (b AccessBinding) Validate() error
type AccessBindingID ¶
type AccessBindingID string
type AccessBindingStatus ¶
type AccessBindingStatus string
AccessBindingStatus — enum lifecycle (migration 0011 CHECK).
const ( AccessBindingStatusPending AccessBindingStatus = "PENDING" AccessBindingStatusActive AccessBindingStatus = "ACTIVE" AccessBindingStatusRevoked AccessBindingStatus = "REVOKED" )
func (AccessBindingStatus) Validate ¶
func (s AccessBindingStatus) Validate() error
type AccessKey ¶ added in v0.5.0
type AccessKey struct {
ID AccessKeyID
UserID UserID
// CredentialID — идентификатор удостоверения WebAuthn: байты нормы,
// глобально уникальны; наружу не выходят (Р10).
CredentialID []byte
// PublicKey — открытый ключ в форме COSE_Key, байт в байт как принят.
PublicKey []byte
// Algorithm — идентификатор алгоритма COSE.
Algorithm int64
// SignCount — сохранённое значение счётчика подписи (Р6).
SignCount uint32
// UserHandle — рукоятка `user.id`, которую церемония регистрации положила
// в ЭТОТ ключ: байты `CeremonyHandle` человека (64 случайных байта, Ф13
// Р3). С ней сверяется каждое утверждение ключа (Ф13-06 «з»).
UserHandle []byte
Name AccessKeyName
Description AccessKeyDescription
CreatedAt time.Time
// LastUsedAt — момент последнего успешного предъявления; нулевой указатель
// — предъявлений не было.
LastUsedAt *time.Time
// Discoverability — признак обнаружимости удостоверения (Р12): сообщается
// аутентификатором ОДИН раз, в церемонии регистрации, и после неё его
// негде прочитать заново. Читатель — страж последнего способа входа.
Discoverability Discoverability
}
AccessKey — строка ключа.
func (AccessKey) Presentable ¶ added in v0.5.0
Presentable — годен ли ключ к предъявлению на полосе входа без пароля: тогда и только тогда, когда его обнаружимость ПОДТВЕРЖДЕНА. «Не сообщено» и «подтверждённо необнаружим» не годны — правило консервативно (Р12).
type AccessKeyChallenge ¶ added in v0.5.0
type AccessKeyChallenge struct {
// Challenge — случайные байты; ключ строки.
Challenge []byte
// UserID — кому выдано: из чужой сессии испытание не находится (Ф7-55).
UserID UserID
Purpose AccessKeyChallengePurpose
// IssuedAt / ExpiresAt — момент выдачи и предел срока.
IssuedAt time.Time
ExpiresAt time.Time
// ConsumedAt — момент предъявления; нулевой указатель — не предъявлено.
ConsumedAt *time.Time
}
AccessKeyChallenge — выданное испытание: привязано к ВЫЗЫВАЮЩЕМУ (Р11), однократно (Ф7-03, Ф7-53) и срочно (Ф7-34, Ф7-54). Срок у обеих процедур — одна величина контракта (§7 инв. 5).
func (AccessKeyChallenge) StateAt ¶ added in v0.5.0
func (c AccessKeyChallenge) StateAt(now time.Time) AccessKeyChallengeState
StateAt — состояние испытания на момент `now`.
func (AccessKeyChallenge) Validate ¶ added in v0.5.0
func (c AccessKeyChallenge) Validate() error
Validate — самопроверка записываемого испытания.
type AccessKeyChallengePurpose ¶ added in v0.5.0
type AccessKeyChallengePurpose string
AccessKeyChallengePurpose — процедура, для которой выдано испытание. Испытание регистрации утверждением не предъявить и наоборот: словарь закрыт ограничением схемы.
const ( // ChallengeForRegistration — испытание церемонии регистрации (§7.1). ChallengeForRegistration AccessKeyChallengePurpose = "registration" // ChallengeForAssertion — испытание предъявления (§7.2). ChallengeForAssertion AccessKeyChallengePurpose = "assertion" )
func AccessKeyChallengePurposes ¶ added in v0.5.0
func AccessKeyChallengePurposes() []AccessKeyChallengePurpose
AccessKeyChallengePurposes — закрытый перечень.
type AccessKeyChallengeState ¶ added in v0.5.0
type AccessKeyChallengeState string
AccessKeyChallengeState — состояние испытания регистрации на момент предъявления: три различимых состояния (Ф7-34), плюс «выдано».
const ( // ChallengeIssued — выдано, не предъявлено, срок не вышел. ChallengeIssued AccessKeyChallengeState = "issued" // ChallengeUnknown — служба такого не выдавала (Ф7-02). ChallengeUnknown AccessKeyChallengeState = "unknown" // ChallengeConsumed — уже предъявлено (Ф7-03). ChallengeConsumed AccessKeyChallengeState = "consumed" // ChallengeExpired — срок вышел (Ф7-34). ChallengeExpired AccessKeyChallengeState = "expired" )
type AccessKeyDescription ¶ added in v0.5.0
type AccessKeyDescription string
AccessKeyDescription — описание ключа: свободный текст с пределом в ЗНАКАХ.
Тип свой, а не `Description` соседей, потому что у них проверка входа считает БАЙТЫ (`len > 256`), а схема — знаки: на кириллической метке две формы расходятся — 200 знаков есть 400 байт. Здесь единица одна — знак, как у схемы и у фундаментного валидатора `corevalidate.Description` (Р10). Расхождение у соседей — их предмет, здесь оно только названо.
func (AccessKeyDescription) Validate ¶ added in v0.5.0
func (d AccessKeyDescription) Validate() error
Validate — предел в рунах.
type AccessKeyID ¶ added in v0.5.0
type AccessKeyID string
AccessKeyID — платформенный идентификатор ключа (Р10). Единственное, чем ключ адресуется извне.
func (AccessKeyID) Validate ¶ added in v0.5.0
func (id AccessKeyID) Validate() error
Validate — форма собственного идентификатора: то, что БУДЕТ ЗАПИСАНО.
type AccessKeyLoginChallenge ¶ added in v0.5.0
type AccessKeyLoginChallenge struct {
// Challenge — случайные байты; ключ строки. Длина — та же, что у
// испытаний церемоний (`AccessKeyChallengeBytes`).
Challenge []byte
// FormContext — контекст формы, под которым испытание выдано. Одно ЖИВОЕ
// испытание на контекст держит частичная уникальность схемы, а не
// проверка перед вставкой (ban #10).
FormContext string
}
AccessKeyLoginChallenge — выдаваемое испытание полосы входа: привязано к контексту формы (Р2), однократно (Ф13-08) и срочно.
func (AccessKeyLoginChallenge) Validate ¶ added in v0.5.0
func (c AccessKeyLoginChallenge) Validate() error
Validate — самопроверка выдаваемого испытания.
type AccessKeyName ¶ added in v0.5.0
type AccessKeyName string
AccessKeyName — имя ключа одной формы дерева (DNS label RFC 1123). Форма — у фундамента; здесь тип для колонки, чтобы «что записывается» судилось тем же валидатором, что у всех именуемых ресурсов службы.
func (AccessKeyName) Validate ¶ added in v0.5.0
func (n AccessKeyName) Validate() error
Validate судит записываемое имя: пустое до записи не доживает (`corevalidate.NameOrDefault`), поэтому здесь оно негодно.
type AccessTarget ¶
type AccessTarget struct {
AllInScope bool // whole-anchor grant (incl. future objects)
Resources []ResourceRef // per-object grant (mutually exclusive with AllInScope)
}
AccessTarget — выбор объектов выдачи (F8). Значима ровно одна ветвь; нулевое значение — выдача на весь якорь (смысл AllInScope).
func (AccessTarget) Contains ¶
func (t AccessTarget) Contains(dottedType, id string) bool
Contains сообщает, перечисляет ли закрытый пообъектный перечень цели объект (dottedType, id). Это проверка членства наименьших привилегий: совпадение по role.rules материализует ТОЛЬКО объект, который цель тоже перечисляет. Цель AllInScope или пустая явных объектов здесь не перечисляет (её ширина — весь якорь, его материализует ветвь «вся область» реконсайлера, а не эта проверка), поэтому Contains для неё ложен; вызывающий судит IsEmpty()/AllInScope до неё.
func (AccessTarget) Digest ¶
func (t AccessTarget) Digest() string
Digest returns a deterministic, set-based canonicalization of the target for the active-grant partial UNIQUE. AllInScope / empty → "all"; a resource set → a hash of its SORTED "type:id" members (order-independent — the same set in any order collides, IAM-1-29).
func (AccessTarget) DistinctTypes ¶ added in v0.6.0
func (t AccessTarget) DistinctTypes() []string
DistinctTypes — различные типы пообъектной цели в порядке первого появления. Им транзакция записи выдачи спрашивает каталог одним оператором.
func (AccessTarget) FirstNonLiveType ¶ added in v0.6.0
func (t AccessTarget) FirstNonLiveType(cat TargetTypeCatalog) (string, bool)
FirstNonLiveType — первый в порядке перечня тип пообъектной цели, у которого нет живой строки каталога; ok=false, когда таких нет (в том числе у цели «вся область», у которой типов нет вовсе). Отказ называет ПЕРВЫЙ неживой тип (Р3): перечень отвергается целиком, и вызывающему нужен один исправимый адрес.
func (AccessTarget) IsEmpty ¶
func (t AccessTarget) IsEmpty() bool
IsEmpty сообщает, что не задана ни одна ветвь (явного выбора нет). Пустая цель хранится и сверяется как AllInScope, но публичный Create её отвергает (цель ОБЯЗАТЕЛЬНА).
func (AccessTarget) ResourceIDsForTypes ¶
func (t AccessTarget) ResourceIDsForTypes(types []string) []string
ResourceIDsForTypes возвращает идентификаторы пообъектных элементов цели, чей точечный тип входит в `types` (порядок сохранён, повторы сняты). По ним реконсайлер находит кандидатов ARM_ANCHOR пообъектной цели по идентификатору (MatchByIDs), а не обходом всей области (MatchAllInScope) — путь материализации наименьших привилегий (IAM-1-21). Пусто, если ни один элемент не подходит по типу.
func (AccessTarget) Validate ¶
func (t AccessTarget) Validate() error
Validate судит форму цели: ветви взаимоисключающие, у каждого пообъектного ResourceRef непустой тип, а идентификатор непуст и подчиняется грамматике объекта (`validObjectID`). AllInScope и пустая цель всегда годны.
Отказы копятся по всем элементам; у элемента с пустым идентификатором отказ один — «(empty)», проверка формы его не дублирует.
Членство типа в каталоге здесь НЕ судится: домен чист и каталога не видит. Годность типа — вопрос живого каталога на момент записи (`ValidTargetType`, `FirstNonLiveType`); его задаёт use-case создания выдачи синхронно по снимку и повторно в транзакции записи (kaname#904).
type Account ¶
type Account struct {
ID AccountID
Name AccountName
Description Description
Labels Labels
OwnerUserID UserID
CreatedAt time.Time
}
Account — top-level tenant («организация» как товар продукта IAM). Замещает прежнюю связку tenant+folder из retired kacho-resource-manager. Уникальное имя глобально (DB UNIQUE accounts_name_unique).
FK: owner_user_id → users(id) ON DELETE RESTRICT. Удаление RESTRICT при наличии Project / ServiceAccount / Group / custom Role.
func (Account) StructuralFacts ¶
func (a Account) StructuralFacts() []StructuralTuple
StructuralFacts returns the two structural facts the model reads on `account`:
cluster:<singleton> → cluster → account:<id> (levels 1-2 reach it here) user:<owner> → owner → account:<id>
The owner is a structural fact and not merely a materialized grant: an account is created by self-service, so tearing it down has to be as reliable as creating it, which is why `account`'s five verbs read `or owner` directly.
type AccountID ¶
type AccountID string
AccountID / ProjectID / ... — newtypes over string, never bare ids.
type AccountName ¶
type AccountName string
Every newtype below has its own regex.
func (AccountName) Validate ¶
func (n AccountName) Validate() error
type AssertionClient ¶
type AssertionClient struct {
// ID — НАШ идентификатор строки реестра. Им и только им клиент себя
// называет: он же издатель и субъект утверждения.
ID string
// Kind — из какой таблицы реестра строка прочитана.
Kind AssertionClientKind
// OwnerID — принципал, от чьего имени клиент получает токен.
OwnerID string
// PublicKeyPEM — зарегистрированный открытый ключ.
PublicKeyPEM string
// Algorithm — ЗАРЕГИСТРИРОВАННЫЙ алгоритм. Пустое значение — законный вход
// схемы, и означает оно «ключа нет», а НЕ «любой алгоритм».
Algorithm string
// ExpiresAt — момент истечения клиента в Unix-секундах; 0 означает
// «бессрочно», и это законное состояние схемы (колонка допускает NULL).
ExpiresAt int64
// Отзыв ключа НЕ является здесь полем, и это не упущение: колонки отзыва в
// схеме обеих таблиц НЕТ — отзыв выражен СНЯТИЕМ СТРОКИ. Завести поле
// значило бы описать состояние, которого схема не допускает, и построить
// на нём пробу, которую нечем поставить (F1 §14 п. 2). Отозванный клиент
// не резолвится вовсе, и отказ наступает на том же шаге, что для
// несуществующего, — то есть требование «отозванный токена не получает»
// выполняется by construction, а не проверкой поля.
// OwnerActive — владелец в состоянии ACTIVE. Не-ACTIVE — это ДВА значения
// словаря (PENDING и BLOCKED), и оба обязаны давать один исход.
OwnerActive bool
// DeclaredAudiences — сужение адресатов, ОБЪЯВЛЕННОЕ заказчиком ключа при
// его выдаче (`IssueSAKeyRequest.audience`, задача kacho#1136).
//
// ПУСТОЙ ПЕРЕЧЕНЬ ОЗНАЧАЕТ «СУЖЕНИЯ НЕ ОБЪЯВЛЕНО», А НЕ «ЛЮБОЙ АДРЕСАТ», и
// это НЕ тот класс, что пустой список доверенных отправителей. Там пустое
// значение снимало единственный контроль; здесь оно оставляет ВНЕШНЮЮ
// границу — объявленный посадкой перечень адресатов, который страж старта
// требует непустым и без которого выдача не строится вовсе. Сужение ключа
// действует ВНУТРИ этой границы и никогда её не расширяет: адресат, которого
// посадка не объявила, не выдаётся ни одному ключу, как бы он ни объявился.
//
// У клиента пользовательского токена перечня нет BY CONSTRUCTION: адресата
// его токена проставляет сам сервис из своей настройки, и заказчик не вправе
// назвать его вовсе (решение Р2, `interactive_client.proto`). Поэтому сужать
// там нечем, и пустое значение на том пути — не умолчание, а отсутствие
// предмета.
DeclaredAudiences []string
}
AssertionClient — строка реестра, способная предъявить утверждение.
Зеркального значения (идентификатора клиента во внешнем сервере) здесь НЕТ, и это не упущение: оно не участвует в разрешении клиента ни как второй ключ поиска, ни как запасной (приёмка F2 §2.1). Субъект с двумя именами дал бы две записи журнала об одном действии и два ключа однократности на одно утверждение — то есть отменил бы однократность, сохранив её форму.
func (AssertionClient) CanPresentAssertion ¶
func (c AssertionClient) CanPresentAssertion() bool
CanPresentAssertion отвечает, способна ли строка вообще участвовать в аутентификации утверждением.
Пустой зарегистрированный алгоритм и пустой ключ — «ключа нет». Это тот же класс, что пустой список доверенных отправителей: значение, которое может быть пустым и не проверено на непустоту, означает «не сужаем».
type AssertionClientKind ¶
type AssertionClientKind string
AssertionClientKind — вид клиента, способного доказать владение ключом.
Словарь ЗАКРЫТ и содержит ровно те виды, у которых в схеме есть ключевой материал. Интерактивного клиента здесь нет НАМЕРЕННО: у него нет способа доказать владение ключом by construction, поэтому он не резолвится ни во что, и отказ наступает на том же шаге, что и для несуществующего клиента (приёмка F2 §2.9). Формулировка «нашли строку, ключ пуст» потребовала бы, чтобы кто-то поддерживал в третьей таблице инвариант «ключа быть не должно»; инвариант, который надо поддерживать, ломается, а выраженный отсутствием таблицы на пути — нет.
const ( // AssertionClientUser — клиент, через который выдаётся токен пользователя. AssertionClientUser AssertionClientKind = "user" // AssertionClientServiceAccount — клиент ключа служебной учётки. AssertionClientServiceAccount AssertionClientKind = "service_account" )
func AssertionClientKinds ¶
func AssertionClientKinds() []AssertionClientKind
AssertionClientKinds возвращает закрытый словарь целиком.
type AssignableRole ¶
type AssignableRole struct {
RoleID RoleID
Name RoleName
Description Description
IsSystem bool
ScopeGroup RoleScopeGroup
CreatedAt time.Time
}
AssignableRole — the lean projection of a Role for the grant-form picker Carries only publicly-safe fields the UI needs to render + group the picker (id / resolved name / description / is_system / server-computed scope_group / created_at) — NO permissions array and no infra-sensitive fields. ScopeGroup is computed via ScopeGroupOf.
type AuditEventID ¶
type AuditEventID string
AuditEventID — идентификатор записи журнала: `evt_` плюс 20…30 символов crockford-base32 (ограничение `audit_outbox_id_check`, миграция `0001_initial.sql`).
Тело СЛУЧАЙНО, а не производно от времени: производитель (`pg.newAuditEventID`) берёт 14 байт у источника случайности и печатает из них 22 символа. Порядок по идентификатору поэтому НЕ является порядком по времени; сортировать журнал надо по `created_at`. Прежняя редакция называла его ULID — то есть обещала ровно ту сортируемость, которой нет.
func (AuditEventID) Validate ¶
func (id AuditEventID) Validate() error
type AuditOutboxEntry ¶
type AuditOutboxEntry struct {
ID AuditEventID
EventType EventTypeName
TenantAccountID *AccountID
EventPayload json.RawMessage
Status AuditOutboxStatus
Attempts int
CreatedAt time.Time
NextAttemptAt time.Time
}
AuditOutboxEntry — строка журнала аудита `kaname.audit_outbox` (заведён миграцией `0001_initial.sql`), дописываемая только вперёд.
Что происходит на самом деле ¶
Строка ложится в ТУ ЖЕ транзакцию, что и мутация домена, и оттуда вывозится в приёмник журнала — поток структурных записей службы (`cmd/kaname/audit_shipper_wiring.go`, механизм — `pkg/audit`). Состояние строки после этого помечено доставленным, а состояние очереди целиком снимает периодический сканер (`cmd/kaname/outbox_metrics_wiring.go`).
Здесь было описано ОТСУТСТВИЕ доставки — у него больше нет предмета ¶
Прежняя редакция объясняла, почему дренаж неконструируем: приёмника аудита не существовало ни одного, и из четырёх объявленных состояний достижимо было ровно одно. Оба утверждения были верны на день записи и перестали быть верными вместе с приёмником (kacho#812): состояний теперь ДВА и оба достижимы, потому что словарь сужен до того, что продукт производит.
Здесь стояло описание, неверное ЧЕТЫРЕЖДЫ ¶
Прежняя редакция объявляла: дренаж отправляет строки в топик брокера; журнал заведён миграцией 0013; идентификатор — ULID; ULID сортируется по времени. Верно из этого ноль. Брокера в продукте нет и он запрещён non-negotiable #7; миграция 0013 — про снятие перечня условий обхода; идентификатор собирается из СЛУЧАЙНЫХ байт (`newAuditEventID`, 22 символа crockford-base32), то есть по времени не сортируется ни в каком порядке. Опасен был не сам текст, а его направление: он описывал систему СЛОЖНЕЕ и исправнее, чем она есть, поэтому читатель уходил искать дренаж и топик вместо того, чтобы увидеть, что доставки нет вовсе.
func (AuditOutboxEntry) Validate ¶
func (e AuditOutboxEntry) Validate() error
type AuditOutboxStatus ¶
type AuditOutboxStatus string
AuditOutboxStatus — состояние ДОСТАВКИ строки журнала.
Значений ровно два, и столько же допускает ограничение таблицы (миграция `20260823001500_audit_journal_gets_its_receiver.sql`). Прежде их объявлялось четыре: «в полёте» и «отказ» не писал никто и никогда — полёта не существует, потому что строка держится блокировкой своей транзакции от клейма до пометки, а терминального отказа не существует, потому что у приёмника нет класса «не приму никогда». Значение, которого продукт произвести не умеет, обещает подсистему, которой нет.
const ( AuditOutboxStatusPending AuditOutboxStatus = "pending" AuditOutboxStatusSent AuditOutboxStatus = "sent" )
func (AuditOutboxStatus) Validate ¶
func (s AuditOutboxStatus) Validate() error
type BasicCredential ¶
type BasicCredential struct {
// PrincipalType — "user" | "service_account".
PrincipalType string
PrincipalID string
DisplayName string
// CredentialID — идентификатор СТРОКИ; им адресуется отзыв.
CredentialID string
ExpiresAt time.Time
}
BasicCredential — вердикт авторитета о годном предъявленном удостоверении.
type BasicCredentialRefusalReason ¶ added in v0.5.0
type BasicCredentialRefusalReason string
BasicCredentialRefusalReason — ПРИЧИНА единого отказа полосы базового секрета (задача kaname#379). Закрытый словарь.
Почему причина есть, а отказ всё равно один ¶
Наружу полоса отвечает ОДНИМ отказом — ErrBasicCredentialRefused: различимый исход был бы оракулом. Но отказ, неотличимый и внутри, делает каждый контроль полосы ненаблюдаемым: отсечка отзыва-всех, сработавшая тысячу раз, выглядит так же, как перебор секрета, и так же, как контроль, не сработавший ни разу.
Поэтому причина едет ВНУТРЬ — значением ошибки (RefuseBasicCredential) — и читается только BasicCredentialRefusalReasonOf. Текст ошибки остаётся дословно текстом единого отказа: вызывающий, напечатавший её, различия не увидит, а на провод полоса отдаёт свой фиксированный текст и вовсе не значение ошибки.
const ( // BasicRefusalMalformed — предъявленное не называет удостоверения, которое // у нас может быть: пусто, форма или контрольная сумма не сходятся, вид по // приставке идентификатора не наш. База не спрашивалась. BasicRefusalMalformed BasicCredentialRefusalReason = "malformed" // BasicRefusalNotFound — живой строки нет: отозвано, истекло, владелец // неактивен либо её не было никогда. Внутри эти случаи тоже неразличимы, и // это не упущение: их отсекает один предикат одного оператора, а не // развилка, и развести их значило бы завести второй запрос. BasicRefusalNotFound BasicCredentialRefusalReason = "not-found" // BasicRefusalSecretMismatch — строка есть, секрет не тот. BasicRefusalSecretMismatch BasicCredentialRefusalReason = "secret-mismatch" // BasicRefusalOwnerRevoked — удостоверение выдано не позже отсечки // отзыва-всех его владельца-человека. Имя то же, что у исхода полосы ключа // токен-эндпоинта: один контроль — одно имя на всех полосах, которые его // исполняют. BasicRefusalOwnerRevoked BasicCredentialRefusalReason = "owner-revoked" // BasicRefusalOwnerUnverified — владелец-человек удостоверения не // подтвердил адрес (kaname#456, Р5). Имя то же, что у исхода полосы ключа // токен-эндпоинта: один вердикт правила выдачи — одно имя. BasicRefusalOwnerUnverified BasicCredentialRefusalReason = "owner-unverified" )
func BasicCredentialRefusalReasonOf ¶ added in v0.5.0
func BasicCredentialRefusalReasonOf(err error) (BasicCredentialRefusalReason, bool)
BasicCredentialRefusalReasonOf — причина отказа полосы.
false — причина не названа: ошибка не отказ полосы либо отказ отдан голым сторожевым. Это отдельный исход, а не причина по умолчанию: подставленная причина выглядела бы фактом и легла бы в чужую клетку.
func BasicCredentialRefusalReasons ¶ added in v0.5.0
func BasicCredentialRefusalReasons() []BasicCredentialRefusalReason
BasicCredentialRefusalReasons — закрытый словарь целиком, в объявленном порядке. Читатели словаря (перепись исходов, витрина) выводят свои клетки отсюда, а не выписывают их второй копией.
type BearerDigest ¶ added in v0.5.0
type BearerDigest string
BearerDigest — свёртка носителя в хранилище (шестнадцатеричная запись SHA-256). Сама по себе носителем не является: обратно значение не восстанавливается.
type CeremonyContext ¶ added in v0.5.0
type CeremonyContext struct {
FamilyID string
ClientID string
UserID string
SessionID string
Scope []string
}
CeremonyContext — контекст церемонии: он один на семейство, код и всякий обновляющий токен семейства. Расхождение его копий отвергает СОСТАВНОЙ внешний ключ схемы, а не проверка писателя.
func (CeremonyContext) Validate ¶ added in v0.5.0
func (c CeremonyContext) Validate() error
Validate — контекст заполнен целиком. Пустая область исходом не является: это не «все права», а отсутствие решения.
type CeremonyHandle ¶ added in v0.5.0
type CeremonyHandle struct {
// contains filtered or unexported fields
}
CeremonyHandle — рукоятка человека: случайные байты, ни с чем не связанные. Нулевое значение — «рукоятки нет», а не рукоятка.
func NewCeremonyHandle ¶ added in v0.5.0
func NewCeremonyHandle() (CeremonyHandle, error)
NewCeremonyHandle — производитель новой рукоятки. Входа у него нет by construction: значение, выводимое из чего бы то ни было о человеке, этой подписью не выражается.
func RestoreCeremonyHandle ¶ added in v0.5.0
func RestoreCeremonyHandle(stored []byte) (CeremonyHandle, error)
RestoreCeremonyHandle — рукоятка из значения, сохранённого ранее; негодная форма — ошибка, а не рукоятка. Только для адаптера хранилища.
func (CeremonyHandle) Bytes ¶ added in v0.5.0
func (h CeremonyHandle) Bytes() []byte
Bytes — копия байтов рукоятки: единственный выход значения наружу.
func (CeremonyHandle) CarriesNoNameOf ¶ added in v0.5.0
func (h CeremonyHandle) CarriesNoNameOf(u User) error
CarriesNoNameOf — ЗАМОК: рукоятка не несёт ни одного ИДЕНТИФИКАТОРА человека — ни платформенного `id`, ни адреса почты.
Проверяется ВХОЖДЕНИЕ, а не равенство: подставленный адрес, дополненный до длины рукоятки, равенства не дал бы, а уехал бы так же безвозвратно. Регистр не различается: адрес хранится приведённым, а в церемонию попадает как есть.
Отображаемое имя НЕ судится, и это решение, а не пропуск. Вхождение имеет смысл, только пока случайные 64 байта его практически не несут: `id` — 20 знаков, адрес — не короче пяти (`x@y.z`), и случайное вхождение того и другого — порядка 1e-9 и меньше на чеканку. Имя бывает в один символ, и его вхождение случайно — около 40% чеканок (замер: 7922 из 20000 для «A»); рукоятка после заведения не меняется, поэтому такой отказ был бы вечным отказом человеку в регистрации. Имя к тому же не идентификатор: им ничего не ключуют, и в церемонии оно стоит законно в соседнем поле.
func (CeremonyHandle) IsZero ¶ added in v0.5.0
func (h CeremonyHandle) IsZero() bool
IsZero — рукоятки нет.
func (CeremonyHandle) Validate ¶ added in v0.5.0
func (h CeremonyHandle) Validate() error
Validate — длина нормы и не нули. Все нули означали бы, что значение не чеканили, а забыли, — это отличимо от негодной длины, потому что чинится другим.
type ClientDescription ¶ added in v0.5.0
type ClientDescription struct {
// contains filtered or unexported fields
}
ClientDescription — приведённое описание клиента; нулевое значение — описания нет.
func NewClientDescription ¶ added in v0.5.0
func NewClientDescription(raw string) ClientDescription
NewClientDescription — приведение сырого значения заголовка (шаги шапки). Пустой вход — отсутствие. Приведение идемпотентно: значение, прочитанное из записи, приводится в себя.
func (ClientDescription) IsZero ¶ added in v0.5.0
func (c ClientDescription) IsZero() bool
IsZero — описания нет.
func (ClientDescription) Value ¶ added in v0.5.0
func (c ClientDescription) Value() string
Value — приведённое значение; у отсутствия — пустая строка.
type Cluster ¶
type Cluster struct {
ID ClusterID
Name ClusterName
Description Description
CreatedAt time.Time
}
Cluster — singleton (id = `cluster_root`). Корень иерархии cluster → account → project → resource. Используется как объект модели прав для `cluster:cluster_root#system_admin@user:usr_xxx`.
type ClusterAdminEntry ¶
type ClusterAdminEntry struct {
ClusterAdminGrantID string
SubjectType string
SubjectID string
SubjectEmail string
SubjectDisplayName string
GrantedByUserID string
GrantedByEmail string // "" when granted_by == "bootstrap"
GrantedAt time.Time
}
ClusterAdminEntry — read-projection of one active cluster-admin grant with denormalised user fields (subject email/display_name, granter email) resolved by the ListActive read-adapter's users JOIN. Pure domain view used by the InternalClusterService.ListAdmins use-case + handler; the pg adapter maps its rows INTO this type so the use-case/handler never import the pgx adapter package (Clean-Architecture dependency rule — the port speaks in domain types).
Denormalised fields (SubjectEmail / SubjectDisplayName / GrantedByEmail) are output-only mirrors; the authoritative subject/granter ids are SubjectID / GrantedByUserID.
type ClusterAdminGrant ¶
type ClusterAdminGrant struct {
ID ClusterAdminGrantID
ClusterID ClusterID
SubjectType GrantSubjectType
SubjectID SubjectID
GrantedBy string // 'bootstrap' либо user_id (verbatim text)
GrantedAt time.Time
GrantedUntil *time.Time // NULL = permanent
}
ClusterAdminGrant — permanent root grant. Один источник истины для FGA-tuple `cluster_admin`.
Partial UNIQUE (subject_type, subject_id) WHERE granted_until IS NULL гарантирует на DB-уровне, что **permanent** grant у одного subject — один. Temporary grants не входят в модель — есть только permanent.
func (ClusterAdminGrant) IsActive ¶
func (g ClusterAdminGrant) IsActive() bool
IsActive — true если grant — permanent active (`granted_until IS NULL`). False — для revoked / expired / time-bombed grants (granted_until set). Используется handler'ом и use-case'ами для diagnostic-веток.
func (ClusterAdminGrant) Validate ¶
func (g ClusterAdminGrant) Validate() error
type ClusterAdminGrantID ¶
type ClusterAdminGrantID string
ClusterAdminGrantID — self-validating newtype, format `cag_<17-crockford>`.
func (ClusterAdminGrantID) Validate ¶
func (id ClusterAdminGrantID) Validate() error
type ClusterID ¶
type ClusterID string
ClusterID — fixed literal `cluster_root` (singleton constraint в DB).
type ClusterName ¶
type ClusterName string
ClusterName — отображаемое имя singleton-кластера: длина 1..64 и БОЛЬШЕ ничего. Здесь стояло «kebab-case», и это было обещанием алфавита, которого проверка не делает ни в одной ветке.
Форму имени ресурса (`pkg/validate/nameform`) поле НЕ несёт намеренно: оно не задаётся клиентом — значение пишет посевная миграция, а у службы кластера нет глагола, который бы его менял.
func (ClusterName) Validate ¶
func (n ClusterName) Validate() error
type CodeDigest ¶ added in v0.5.0
type CodeDigest string
CodeDigest — свёртка кода в хранилище (шестнадцатеричная запись SHA-256).
type ContainerSelector ¶ added in v0.5.0
type ContainerSelector struct {
// Type — точечный тип контейнера (`registry.registries`), словарь каталога.
Type string
// OwnerFP — отпечаток правила-владельца: его селектор и выбирает контейнер.
// По нему разворачивание выдачи классу узнаёт, что контейнер в составе выдачи
// ИМЕННО этим правилом (а не соседним), прежде чем писать `public_reader`.
// В хранимую строку селектора не едет: решению о доступе он не нужен.
OwnerFP string
Arm Arm
ResourceNames []string // ArmNames only
MatchLabels map[string]string // ArmLabels only
}
ContainerSelector — тип и отбор правила-владельца вложенного правила (kaname#874). Ветвь — та же, что у правила верхнего уровня: якорь выбирает все контейнеры типа, имена — названные, метки — с совпавшими метками.
type CredentialKind ¶
type CredentialKind string
CredentialKind — ЧЕМ УДОСТОВЕРЕНИЕ СЕБЯ ПРЕДЪЯВЛЯЕТ (задача kacho#1142, приёмка BAT-1 §2.5, §4.1).
Значение — то же, что лежит в колонке `credential_kind` обеих таблиц удостоверений: второго написания не заводится. Вид ЗАПИСЫВАЕТСЯ при вставке и читателем НЕ вычисляется: правила вывода вида по содержимому строки в службе нет.
Словарь закрыт, и видов в нём три. Четвёртый, LEGACY, описывал строки прежнего потока без собственного ключевого материала и снят вместе со столбцом имени клиента у прежнего издателя (kaname#362): вид был определён через этот столбец, и строк этого вида миграция снятия не допускает.
const ( // CredentialKindUnspecified — вид не назван вызывающим. Встречается ТОЛЬКО // во входе выдачи и разрешается сохранённым поведением; в строке // удостоверения не бывает. CredentialKindUnspecified CredentialKind = "" // CredentialKindKeypair — ключевая пара ES256: вызывающий сам собирает и // подписывает `client_assertion` и обменивает его. CredentialKindKeypair CredentialKind = "KEYPAIR" // CredentialKindSecret — однострочный непрозрачный секрет, предъявляемый // как есть. CredentialKindSecret CredentialKind = "SECRET" // CredentialKindFederated — удостоверение предъявляет ВНЕШНИЙ издатель по // перечню доверенных субъектов; ни материала, ни секрета у нас нет. CredentialKindFederated CredentialKind = "FEDERATED" )
func ResolveIssuedKind ¶
func ResolveIssuedKind(asked CredentialKind, hasTrustedSubjects, federationSupported bool) (CredentialKind, error)
ResolveIssuedKind — КЛАССИФИКАТОР НАД ЗАПРОСОМ ВЫДАЧИ. Ветвей ТРИ, и дыры у него нет by construction: «ни материала, ни перечня» здесь означает KEYPAIR, потому что материал чеканим МЫ — строка получает его при выпуске. Значение вне словаря отвергается с именем поля, а не получает ближайший вид.
asked — вид, названный вызывающим (пустой = не назван). hasTrustedSubjects — непуст ли перечень доверенных субъектов запроса. federationSupported — есть ли у этого глагола поле, которым задаётся федеративный вид (у личности его нет, и вид недостижим by construction).
func (CredentialKind) IsIssuable ¶
func (k CredentialKind) IsIssuable() bool
IsIssuable отвечает, производит ли этот вид хоть один глагол выдачи. Ответ — свойство самого вида, а не проверки в конкретном глаголе: иначе следующий глагол завёл бы выдачу под значение вне словаря.
func (CredentialKind) String ¶
func (k CredentialKind) String() string
String — значение как оно лежит в колонке.
type Description ¶
type Description string
func (Description) Validate ¶
func (d Description) Validate() error
type Discoverability ¶ added in v0.5.0
type Discoverability string
Discoverability — область значений признака обнаружимости: ТРИ состояния (Р12). «Не сообщено» — своё значение, а не пустое и не «нет»: пустая строка состоянием не является и до записи не доживает.
const ( // DiscoverabilityConfirmed — расширение результата церемонии сообщило // «обнаруживаемое» (`credProps.rk = true`). DiscoverabilityConfirmed Discoverability = "discoverable" // DiscoverabilityNotDiscoverable — расширение сообщило «не обнаруживаемое». // Писателя у состояния сегодня нет: приём церемонии такой результат // отвергает (Ф7-41), — но область значений факта его содержит (Ф7-57). DiscoverabilityNotDiscoverable Discoverability = "not-discoverable" // DiscoverabilityUnreported — расширения в результате нет. DiscoverabilityUnreported Discoverability = "unreported" )
func AccessKeyDiscoverabilities ¶ added in v0.5.0
func AccessKeyDiscoverabilities() []Discoverability
AccessKeyDiscoverabilities — закрытый перечень, тот же, что у ограничения схемы `user_access_keys_discoverability_check`.
func DiscoverabilityFromExtension ¶ added in v0.5.0
func DiscoverabilityFromExtension(rk *bool) Discoverability
DiscoverabilityFromExtension — состояние по расширению `credProps.rk`: nil — «не сообщено», а не «нет».
func (Discoverability) Validate ¶ added in v0.5.0
func (d Discoverability) Validate() error
Validate — значение из закрытого перечня.
type DisplayName ¶
type DisplayName string
func (DisplayName) Validate ¶
func (d DisplayName) Validate() error
type EmailChangeCode ¶ added in v0.5.0
type EmailChangeCode struct {
ID EmailChangeCodeID
UserID UserID
// NewEmail — новый адрес в приведённом виде.
NewEmail Email
// Digest — свёртка кода; пусто — новый адрес занят на запросе, кода нет.
Digest CodeDigest
// IssuedAt — момент запроса (часы полосы); по нему считается темп (Р6).
IssuedAt time.Time
// ExpiresAt — абсолютный срок; величина — настройка.
ExpiresAt time.Time
}
EmailChangeCode — запись принятого запроса смены адреса. Состав закрыт.
func (EmailChangeCode) Validate ¶ added in v0.5.0
func (c EmailChangeCode) Validate() error
Validate — запись, годная к записи: судится до базы то, что база держит ограничениями.
type EmailChangeCodeID ¶ added in v0.5.0
type EmailChangeCodeID string
EmailChangeCodeID — идентификатор строки отложенной смены. Наружу не адресуется.
type EventTypeName ¶
type EventTypeName string
EventTypeName — `^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$` (CHECK).
func (EventTypeName) Validate ¶
func (n EventTypeName) Validate() error
type ExternalSubject ¶
type ExternalSubject string // OIDC sub
func NewOwnLaneSubject ¶ added in v0.5.0
func NewOwnLaneSubject() ExternalSubject
NewOwnLaneSubject — свежая идентичность субъекта нашей полосы.
func (ExternalSubject) IsOwnLane ¶ added in v0.5.0
func (s ExternalSubject) IsOwnLane() bool
IsOwnLane — отчеканена ли идентичность нашей полосой (по голове).
func (ExternalSubject) Validate ¶
func (s ExternalSubject) Validate() error
type FamilyRevocationReason ¶ added in v0.5.0
type FamilyRevocationReason string
FamilyRevocationReason — причина отзыва семейства. Словарь ЗАКРЫТ и совпадает с ограничением `token_families_revoked_reason_ck`: корзины «прочее» у него нет, и значение вне перечня — наша ошибка, а не чужая. Совпадение в обе стороны держит проба живой схемы `TestIntegration_RevocationVocabularyAgreesWithTheDomain`, а то, что у каждого слова есть писатель, — гейт `TestFamilyRevocationVocabulary_KN_FRV_17_EveryWordHasAWriter`: слово, которого никто не пишет, перечень превращает в обещание (kaname#339).
const ( // FamilyRevokedByCodeReplay — повторное предъявление кода авторизации. FamilyRevokedByCodeReplay FamilyRevocationReason = "code-replay" // FamilyRevokedByRefreshReplay — повторное предъявление обновляющего токена. FamilyRevokedByRefreshReplay FamilyRevocationReason = "refresh-replay" // FamilyRevokedBySessionEnd — сессия, в которой шла церемония, снята. FamilyRevokedBySessionEnd FamilyRevocationReason = "session-ended" // FamilyRevokedByClientRevocation — клиент, которому выдан грант, сам // попросил отзыва (RFC 7009). Написание — дословно причина фундамента // `oauthceremony.RevocationClientRevoke`: адаптер порта отзыва сопрягает // словари ПО ЗНАЧЕНИЮ (`ceremonyport.FamilyReasonOf`), и другое написание // оставило бы причину без слова (kaname#406). FamilyRevokedByClientRevocation FamilyRevocationReason = "client-revoke" )
func FamilyRevocationReasons ¶ added in v0.5.0
func FamilyRevocationReasons() []FamilyRevocationReason
FamilyRevocationReasons — перечень целиком, КОПИЕЙ: вызывающий не может расширить его на месте.
func (FamilyRevocationReason) Validate ¶ added in v0.5.0
func (r FamilyRevocationReason) Validate() error
Validate — причина из словаря. Пустая причина исходом не является: отзыв без причины неотличим от неотозванного семейства в переписи.
type FeedSource ¶
type FeedSource int8
FeedSource — where the reconciler reads candidate objects for a selectable type (D7).
const ( // FeedMirror — candidates read from kaname.resource_mirror (labels @> // matchLabels); containment from mirror.parent_* (D7). Mirror-fed types can be // PENDING_VERIFICATION (object not yet mirrored). Consumer-owned resources: // compute / vpc / loadbalancer. FeedMirror FeedSource = iota // FeedIAMDirect — candidates read SAME-DB from IAM's own resource table; // containment via iam-hierarchy (project ⊑ account ⊑ cluster). Never PENDING // (the object is always in its own table the instant it exists). Покрывает ВСЕ // iam-native типы (любой `iam.*`): account / project / user / serviceAccount / // group / role / accessBinding — все label-selectable под единой моделью. FeedIAMDirect )
func FeedSourceForType ¶
func FeedSourceForType(objectType string) FeedSource
FeedSourceForType classifies a selectable object type by its feed-source (D6/D7). An `iam.*` type is IAM-direct (own-table same-DB match, iam-hierarchy containment); every other selectable family (compute/vpc/loadbalancer) is mirror-fed. The classification is by module-prefix only — the precise selectable whitelist is enforced separately in the use-case (a non-selectable type never reaches the reconciler).
type FormKind ¶ added in v0.5.0
type FormKind string
FormKind — вид формы, к которому привязан признак защиты от подделки запроса (Р12). Перечень ЗАКРЫТ и объявлен один раз; Ф4 и Ф5 дописывают свои виды сюда, а не заводят второй перечень.
const ( FormLogin FormKind = "login" FormLogout FormKind = "logout" FormPassword FormKind = "password" // FormRegister — форма регистрации (Ф4 Р1; форма защищена признаком, как и // прочие, меняющие состояние, — Ф1-39…43). FormRegister FormKind = "register" // Восстановление доступа (Ф5, `kacho#1271`) — две формы, два вида: запрос // кода и его предъявление с новым паролем. Признак одной не годится другой // (Ф1 Р6): у них разные предметы и разные последствия. FormRecovery FormKind = "recovery" FormRecoveryComplete FormKind = "recovery-complete" // Второй фактор (Ф12, `kacho#1281`, Р4): один вид на четыре глагола // семейства `second-factor/*` — у них один предмет (заведённый фактор // самого человека) — и свой вид у церемонии повышения: её зовут и без // фактора, ветвью пароля (Ф11-09), и признак семейства ей не годится. FormSecondFactor FormKind = "second-factor" FormStepUp FormKind = "step-up" // FormStepUpAccessKeyBegin — выдача испытания повышения ключом доступа под // живой сессией (kaname#827, приёмка SUA Р2): свой вид, а не вид // церемонии — выдача меняет состояние (заводит испытание сессии), и признак // церемонии на форме выдачи не предъявим, и наоборот. FormStepUpAccessKeyBegin FormKind = "step-up-access-key-begin" // Подтверждение адреса (kaname#456, Р6; имена ратифицированы — Ф6 Р15): // запрос письма и предъявление кода — две формы, два вида, и признак одного // другому не годится. FormVerifyEmail FormKind = "verify-email" FormVerifyEmailConfirm FormKind = "verify-email-confirm" // Вход ключом доступа (Ф13 Р1, решение владельца 2026-09-22): ДВА вида на // два глагола `access-key/begin` и `access-key/login`. Оба меняют состояние // — `begin` заводит испытание и считается попыткой, `login` выдаёт сессию, // — и проверка «вид признака — вид ЭТОЙ формы» (Ф3 Р12) обязана их // различать: признак формы запроса на форме подтверждения не предъявим, и // наоборот. Контекст при этом ОДИН (печенье `kaname_form`). FormAccessKeyBegin FormKind = "access-key-begin" FormAccessKeyLogin FormKind = "access-key-login" // FormPasswordEnroll — заведение первого пароля из живой сессии (kaname#213, // приёмка A7 Р1): свой вид, не вид смены — заведение и смена суть разные // действия с разными основаниями. FormPasswordEnroll FormKind = "password-enroll" // Смена адреса почты (kaname#635, приёмка email-change Р1): запрос смены и // предъявление кода с нового адреса — две формы, два вида; признак одного // другому не годится. FormEmailChange FormKind = "email-change" FormEmailChangeConfirm FormKind = "email-change-confirm" // FormSessionEnd — снятие своих записей сессии (kaname#634, Р1): ОДИН вид на // оба снимающих глагола — выход из выбранной и из всех, кроме текущей. Предмет // у них один (снятие своих записей), и признак различает формы, а не исходы — // как у семейства второго фактора. FormSessionEnd FormKind = "session-end" )
func FormKinds ¶ added in v0.5.0
func FormKinds() []FormKind
FormKinds — закрытый перечень видов формы, копией.
func ParseFormKind ¶ added in v0.5.0
ParseFormKind — вид формы из строки запроса; вне перечня — отказ, называющий поле и перечень.
type GrantDelegation ¶ added in v0.7.0
type GrantDelegation struct {
ID GrantDelegationID
AccountID AccountID
// DelegateID — служебная учётка-делегат. Тип делегата один
// (`SubjectTypeServiceAccount`), поэтому хранится только идентификатор.
DelegateID ServiceAccountID
RoleIDs []RoleID
TargetTypes []string
SubjectTypes []SubjectType
// Module — модуль ролей и типов делегирования; выводится, на вход не
// принимается.
Module string
Status GrantDelegationStatus
DelegatedBy GrantDelegationActor
CreatedAt time.Time
RevokedAt *time.Time
RevokedBy *GrantDelegationActor
}
GrantDelegation — делегирование права выдавать.
func SortedGrantDelegationAxes ¶ added in v0.7.0
func SortedGrantDelegationAxes(d GrantDelegation) GrantDelegation
SortedGrantDelegationAxes — оси делегирования по возрастанию строки: оси — наборы, порядок входа не хранится (Р1, `api-repeated-order`).
func (GrantDelegation) Active ¶ added in v0.7.0
func (d GrantDelegation) Active() bool
Active — делегирование действует.
func (GrantDelegation) AllowsRole ¶ added in v0.7.0
func (d GrantDelegation) AllowsRole(id RoleID) bool
AllowsRole — роль входит в набор ролей делегирования.
func (GrantDelegation) AllowsSubjectType ¶ added in v0.7.0
func (d GrantDelegation) AllowsSubjectType(t SubjectType) bool
AllowsSubjectType — тип субъекта выдачи входит в набор типов делегирования.
func (GrantDelegation) AllowsTargetType ¶ added in v0.7.0
func (d GrantDelegation) AllowsTargetType(dotted string) bool
AllowsTargetType — тип объекта цели входит в набор типов делегирования.
func (GrantDelegation) IsDelegate ¶ added in v0.7.0
func (d GrantDelegation) IsDelegate(principalType, principalID string) bool
IsDelegate — принципал (вид, идентификатор) — делегат этого делегирования.
Сравнивается ПАРА, а не голый идентификатор: вид принципала нормализуется одной функцией (`SubjectTypeOfPrincipal`), поэтому человек с совпавшим идентификатором делегатом не считается by construction.
func (GrantDelegation) IsDelegateSubject ¶ added in v0.7.0
func (d GrantDelegation) IsDelegateSubject(s Subject) bool
IsDelegateSubject — субъект выдачи — сам делегат (Д4: себе делегат не выдаёт).
type GrantDelegationActor ¶ added in v0.7.0
GrantDelegationActor — принципал шага жизненного цикла: вид ("user" | "service_account") и идентификатор.
type GrantDelegationID ¶ added in v0.7.0
type GrantDelegationID string
GrantDelegationID — идентификатор делегирования.
type GrantDelegationStatus ¶ added in v0.7.0
type GrantDelegationStatus string
GrantDelegationStatus — состояние делегирования; слово хранения.
const ( // GrantDelegationActive — делегирование действует. GrantDelegationActive GrantDelegationStatus = "ACTIVE" // GrantDelegationRevoked — отозвано; конечное состояние. GrantDelegationRevoked GrantDelegationStatus = "REVOKED" )
type GrantSubjectType ¶
type GrantSubjectType string
GrantSubjectType — enum: user|service_account (NOT group: cluster-admin grant — strictly individual identity for audit). Backed by migration 0011 CHECK.
const ( GrantSubjectTypeUser GrantSubjectType = "user" GrantSubjectTypeServiceAccount GrantSubjectType = "service_account" )
func (GrantSubjectType) Validate ¶
func (t GrantSubjectType) Validate() error
type Group ¶
type Group struct {
ID GroupID
AccountID AccountID
Name GroupName
Description Description
Labels Labels
CreatedAt time.Time
}
Group — Account-scoped (account_id FK ON DELETE RESTRICT), имеет members (User или ServiceAccount — полиморфно через `group_members.member_type`). Используется в AccessBinding для упрощения раздачи прав.
type GroupMember ¶
type GroupMember struct {
GroupID GroupID
MemberType SubjectType // user | service_account (group из enum'а исключен)
MemberID SubjectID
AddedAt time.Time
}
GroupMember — связка group_id ↔ (member_type, member_id). Целостность member_id обеспечивается DB-триггером `group_members_member_exists_trg` (нет полиморфного FK в Postgres).
func (GroupMember) Validate ¶
func (m GroupMember) Validate() error
type HumanSession ¶ added in v0.5.0
type HumanSession struct {
ID HumanSessionID
UserID UserID
// AuthenticatedAt — момент аутентификации. Неподвижен всю жизнь записи
// (Ф11 Р6): край сравнивает его с отсечкой.
AuthenticatedAt time.Time
// LastPresentedAt — момент последнего ПРЕДЪЯВЛЕНИЯ СПОСОБА (сверка
// пароля, кода, ключа), не носителя запросу. Читается внутри службы.
LastPresentedAt time.Time
// ExpiresAt — абсолютный срок, ОДИН (F4d-27): окна бездействия нет.
ExpiresAt time.Time
// AssuranceLevel — по правилу Ф11 от множества предъявленного.
AssuranceLevel string
// PresentedMethods — множество предъявленного (Ф11 Р2), слова словаря
// `assurance.Methods`. Читается внутри службы правилом уровня.
PresentedMethods []string
// Client — описание клиента, каким его назвал запрос выдачи (kaname#634,
// Р3). Пишет его ТОЛЬКО операция выдачи; перевыпуск носителя его не трогает.
// Читатель — ровно перечень своих сессий: в ответ краю и в событие аудита оно
// не идёт.
Client ClientDescription
// CreatedAt назначает запись.
CreatedAt time.Time
}
HumanSession — запись сессии (Р1). Состав закрыт: поле без читателя есть значение, которое пишут и не читают.
func (HumanSession) Expired ¶ added in v0.5.0
func (s HumanSession) Expired(now time.Time) bool
Expired — истёк ли срок на данный момент. Граница — включающая: в сам момент срока сессии уже нет.
func (HumanSession) Validate ¶ added in v0.5.0
func (s HumanSession) Validate() error
Validate — запись, годная к выдаче. Судится ДО базы то, что база держит ограничениями: уровень из оси, непустое множество, срок позже момента, последнее предъявление не раньше аутентификации.
type HumanSessionID ¶ added in v0.5.0
type HumanSessionID string
HumanSessionID — идентификатор записи сессии. Адресуется ровно одним внешним путём — её ВЛАДЕЛЬЦЕМ на полосе формы (kaname#634, приёмка `own-sessions-are-listed-and-ended-by-their-owner.md`, Р4, Р5): перечень своих сессий его называет, выход из выбранной его принимает. Края он достигает ровно одним путём — ответом `Resolve` о носителе, и край возвращает его службе метаданным доверенного отправителя, чтобы снятие ключа доступа оставило текущую запись живой (Ф13 Р8, kaname#677). Носителем он не является и о субъекте ничего не говорит; субъект глагола — всегда субъект носителя запроса (у снятия ключа — переданная личность).
func ParseHumanSessionID ¶ added in v0.5.0
func ParseHumanSessionID(s string) (id HumanSessionID, ok bool)
ParseHumanSessionID — идентификатор записи, присланный клиентом; форма судится ДО хранилища. Пустое значение — отказ «required» — вызывающий судит раньше этого разбора; здесь ok=false значит «не та форма».
type InteractiveClient ¶
type InteractiveClient struct {
ID InteractiveClientID
CreatedAt time.Time
Name InteractiveClientName
Description Description
Labels Labels
// RedirectURIs — where the provider may deliver an authorization code.
// At least one, every one an absolute https:// URL.
RedirectURIs []string
// PostLogoutRedirectURIs — optional; same https:// rule when present.
PostLogoutRedirectURIs []string
// ClientID — provider-side client id (output-only).
ClientID string
// Audiences — stamped by iam from its own configuration (output-only).
Audiences []string
// GrantTypes — always exactly authorization_code + refresh_token (output-only).
GrantTypes []string
// TokenEndpointAuthMethod — provider-side auth method (output-only).
TokenEndpointAuthMethod string
Status InteractiveClientStatus
}
InteractiveClient — the OAuth2 client through which a HUMAN completes an interactive sign-in ceremony.
Provider-side fields (ClientID, Audiences, GrantTypes, TokenEndpointAuthMethod) are OUTPUT-ONLY: they are decided by iam and the identity provider, never by the caller. Audiences in particular is a decision, not an omission — see Р2 in the acceptance: a field that can be set but cannot be set CORRECTLY is a field without a reader, and one that can be set incorrectly breaks the edge silently.
func (InteractiveClient) Validate ¶
func (c InteractiveClient) Validate() error
Validate — self-validating domain entity.
type InteractiveClientID ¶
type InteractiveClientID string
InteractiveClientID — id of an InteractiveClient. Hyphen canon `ic-<17>` (ids.PrefixInteractiveClientHyphen), immutable for the life of the resource and the only externally addressable identity (core rule #15).
type InteractiveClientName ¶
type InteractiveClientName string
InteractiveClientName — cluster-unique cosmetic label. Judged by the single resource-name form of the tree, so the DB CHECK and this validator agree.
func (InteractiveClientName) Validate ¶
func (n InteractiveClientName) Validate() error
Validate — единственная форма имени (`pkg/validate/nameform`).
type InteractiveClientStatus ¶
type InteractiveClientStatus string
InteractiveClientStatus — lifecycle status.
const ( InteractiveClientActive InteractiveClientStatus = "ACTIVE" InteractiveClientDeleting InteractiveClientStatus = "DELETING" )
InteractiveClientStatus values. Mirrored by a DB CHECK.
func (InteractiveClientStatus) Validate ¶
func (s InteractiveClientStatus) Validate() error
Validate — the status is one of the two named values.
type InviteStatus ¶
type InviteStatus string
InviteStatus — invite-flow state for a User row.
PENDING — created via `UserService.Invite`, external_id="" until first login; the invitee has not yet registered and verified the address. ACTIVE — either self-registration on our sign-in lane without a pending invite, or a PENDING row activated by confirming the invited address. BLOCKED — административный запрет на членство в Account'е. Ставится и снимается ДЕЙСТВИЯМИ `UserService.Block` / `Unblock` (право `identity_suspender@iam_user` = админ аккаунта плюс каскад облака; `v_update` с этого типа снят, kacho#1128); писатель — `userWriter.SetInviteStatus`.
Состояние принадлежит СТРОКЕ ЧЛЕНСТВА, а не человеку. Одна личность держит по строке на каждый Account, поэтому запрет принадлежит тому аккаунту, который его наложил, и не отключает личность там, где она законно активна: выдача токена перебирает набор членств и обслуживает первое аутентифицирующееся, отказывая лишь когда ни одно не может (token_enrichment_service.go).
Снимать запрет самостоятельным действием нельзя: восстановление пароля доказывает владение почтовым ящиком — ровно то, чего администратор, ставя запрет, под сомнение не ставил (см. humansession/recovery_complete.go). Поэтому у пути блокировки ОБЯЗАН быть административный путь снятия, иначе заблокированный окажется заперт навсегда. Гейт blocked_state_reachability_test.go требует, чтобы каждый писатель этого состояния был объявлен вместе со ссылкой на снятие, — и делает появление одностороннего пути упавшей сборкой, а не открытием.
const ( InviteStatusPending InviteStatus = "PENDING" InviteStatusActive InviteStatus = "ACTIVE" InviteStatusBlocked InviteStatus = "BLOCKED" )
func (InviteStatus) MayAuthenticate ¶
func (s InviteStatus) MayAuthenticate() bool
MayAuthenticate reports whether a user in this state may be issued a token.
This is the VERDICT both token hooks ask for, and the reason it exists as a predicate rather than as a WHERE clause: the hooks used to resolve the identity through an ACTIVE-only query, which turns "blocked" into "absent" — and then read absence in opposite directions. One refused; the other took it for "the mirror has not committed yet" and issued the reduced claim set to a blocked user. A filter cannot be asked "why"; a verdict can.
Only ACTIVE authenticates. PENDING is an invitee who has not confirmed an identity yet (the DB CHECK users_invite_status_consistency keeps such a row from carrying an external id at all, so it is a floor rather than a live path), and an unset state is not an authorisation.
func (InviteStatus) MaySignInToOwnLane ¶ added in v0.5.0
func (s InviteStatus) MaySignInToOwnLane() bool
MaySignInToOwnLane reports whether a person in this state may sign in on OUR sign-in lane and hold a session there (kaname#456, Р11 п. 1). Wider than InviteStatus.MayAuthenticate by exactly one state: an invitee who registered by the invite's address (PENDING) signs in to the verification position — the only screens a session reaches before the address is confirmed — while a token is issued to nobody in that position. BLOCKED never signs in.
func (InviteStatus) Validate ¶
func (s InviteStatus) Validate() error
type LimitKind ¶
type LimitKind string
LimitKind — a dotted token naming what is being counted. Two forms, and only two:
`<domain>.<resource>` — a resource counted in its carrier `<domain>.<parent>.<child>` — how many <child> fit in ONE <parent>
Both forms name REAL types of the authorization model, and the three-part form names two of them. That is a gate rather than a convention: a ceiling stated on a name the platform does not know is a ceiling nobody can check and nobody can show the tenant (§7 п.9 of the acceptance).
func AccountScopedKinds ¶
func AccountScopedKinds() []LimitKind
AccountScopedKinds отдаёт КОПИЮ перечня в устойчивом порядке.
func IdentityCarriedKinds ¶ added in v0.3.0
func IdentityCarriedKinds() []LimitKind
IdentityCarriedKinds — копия закрытого перечня, в порядке объявления.
func PostureStatedKinds ¶
func PostureStatedKinds() []LimitKind
PostureStatedKinds — копия закрытого множества, в порядке объявления.
func (LimitKind) ChildKind ¶
ChildKind — the two-part token of the child a nested kind counts; empty for a flat kind. `vpc.network.subnet` → `vpc.subnet`.
The child's domain is the kind's domain: a nested kind never crosses a service boundary, because the parent and the child are rows of one database and the count is an invariant of one schema (data-integrity §within-service).
func (LimitKind) Nested ¶
Nested reports whether this kind bounds children within ONE parent (`vpc.network.subnet`) rather than within the carrier as a whole.
func (LimitKind) ParentKind ¶
ParentKind — the two-part token of the parent a nested kind counts within; empty for a flat kind. `vpc.network.subnet` → `vpc.network`.
This is the token that must resolve against the closed table, and it is returned rather than re-derived at each call site so the two halves of the three-part gate cannot disagree about where the split is.
type LoginMethod ¶ added in v0.5.0
type LoginMethod struct {
UserID UserID
Kind LoginMethodKind
Verifier LoginVerifier
// State — состояние строки (Ф12 Р1). Обязательно у всякого вида: значение,
// подставленное молча, было бы вторым местом об одном предмете рядом с
// умолчанием колонки.
State LoginMethodState
// AcceptedStep — последний принятый шаг кода по времени (Ф12 Р5);
// StepAccepted отделяет «шага ещё не было» от значения: нулевой шаг —
// законная величина оси, а не отсутствие.
AcceptedStep int64
StepAccepted bool
// CreatedAt назначает запись. У строки `pending` — момент заведения, от
// которого считается срок (Ф12 Р8); у `active` — момент подтверждения.
CreatedAt time.Time
// LastUsedAt — момент последнего ПРИНЯТОГО предъявления способа второго
// фактора (kaname#827, приёмка SUA Р6): у `totp` — принятый код, у
// `lookup_secret` — снятый запасной код. LastUsed отделяет «не
// использовали» от значения: нулевой момент значением оси не является, и
// отсутствие наружу — отсутствие ключа ответа, а не нулевое время. Пишут
// его только операторы предъявления; у пароля момента нет — его не
// допускает проверка схемы.
LastUsedAt time.Time
LastUsed bool
}
LoginMethod — строка способа входа.
Ключ — пара (`UserID`, `Kind`): второй способ того же вида человеку отвергается базой. `UserID` — идентификатор ЧЕЛОВЕКА (`users.id`), а не внешний субъект и не почта: у приглашённого внешнего субъекта нет, почта изменяема.
func (LoginMethod) Enrolled ¶ added in v0.5.0
func (m LoginMethod) Enrolled() bool
Enrolled — строка есть способ входа: заведён и подтверждён (Ф12 Р1).
func (LoginMethod) Validate ¶ added in v0.5.0
func (m LoginMethod) Validate() error
Validate — строка, годная к записи. Отказ не несёт материала.
type LoginMethodKind ¶ added in v0.5.0
type LoginMethodKind string
LoginMethodKind — вид способа входа. Словарь ЗАКРЫТ и совпадает с ограничением таблицы `user_login_methods_kind_check`.
ИМЕНА БЕРУТСЯ У СЛОВАРЯ СПОСОБОВ ПРЕДЪЯВЛЕНИЯ (`internal/assurance`, Ф11 Р8), а не пишутся здесь литералами: словарь объявлен ОДИН раз, и второе объявление перечня имён в прод-коде службы — находка гейта `TestAssuranceMethodVocabularyIsDeclaredOnce`. Видов три (Ф12 Р1): пароль · код по времени · набор запасных кодов; ключ доступа (`webauthn`) строкой этой таблицы не является — его хранилище заводит Ф7 своей приёмкой.
func LoginMethodKinds ¶ added in v0.5.0
func LoginMethodKinds() []LoginMethodKind
LoginMethodKinds — словарь видов; копия, чтобы вызывающий не мог его расширить.
func ParseLoginMethodKind ¶ added in v0.5.0
func ParseLoginMethodKind(s string) (LoginMethodKind, error)
ParseLoginMethodKind — вид из строки. Написание одно: регистр и пробелы не нормализуются, иначе вид становится несколькими значениями.
func (LoginMethodKind) Validate ¶ added in v0.5.0
func (k LoginMethodKind) Validate() error
Validate — вид из словаря.
type LoginMethodState ¶ added in v0.5.0
type LoginMethodState string
LoginMethodState — состояние строки способа (Ф12 Р1). Значений ровно два, закреплены ограничением схемы `user_login_methods_state_check`.
const ( // LoginMethodStatePending — секрет чеканен, первый код не предъявлен. // Способом входа такая строка не является и живёт не дольше окна свежести // правки своих данных (Ф12 Р8); снимает её уборка либо новое заведение. LoginMethodStatePending LoginMethodState = "pending" // LoginMethodStateActive — подтверждён; единственное состояние `password` // и `lookup_secret`. LoginMethodStateActive LoginMethodState = "active" )
func (LoginMethodState) Validate ¶ added in v0.5.0
func (s LoginMethodState) Validate() error
Validate — состояние из словаря.
type LoginVerifier ¶ added in v0.5.0
type LoginVerifier struct {
// contains filtered or unexported fields
}
LoginVerifier — проверочный материал способа входа: хеш пароля в той форме, в какой его создала функция хеширования (прежнего поставщика либо наша). Это СЕКРЕТ: хеш не пароль, но перебор офлайн по нему возможен.
Устройство выбрано так, чтобы материал не уезжал из процесса ни одним общим путём вывода:
- форматирование, журнал и `String` отдают заглушку;
- JSON и текстовая форма ОТКАЗЫВАЮТ;
- материал лежит ЗА УКАЗАТЕЛЕМ. Форматирование обходит неэкспортированное поле, не вызывая методов типа, и печатает его содержимое сырым; указатель на вложенной глубине печатается адресом, а не содержимым. Так тип защищён и там, где его методы не спрашиваются.
Выход у материала ОДИН — `Reveal`. Его вызывающих держит гейт дерева `internal/check` `TestLoginVerifierStaysInside`.
Нулевое значение означает «материала нет» и в строку способа не проходит: отсутствие материала выражается ОТСУТСТВИЕМ строки.
Сравнение через `==` сверяет указатели, а не материал. Сверка предъявленного с хранимым — дело проверяющего (часть П2), и только постоянным временем.
func NewLoginVerifier ¶ added in v0.5.0
func NewLoginVerifier(material string) (LoginVerifier, error)
NewLoginVerifier — материал как есть, ДОСЛОВНО: без обрезки и нормализации. Перенесённый хеш держит вход прежним паролем ровно пока он побайтово тот же (Р1 Ф1). Пустой материал отвергается.
func (LoginVerifier) Format ¶ added in v0.5.0
func (v LoginVerifier) Format(f fmt.State, verb rune)
Format — заглушка на ЛЮБОМ глаголе форматирования, включая `%x` и `%q`.
func (LoginVerifier) GoString ¶ added in v0.5.0
func (v LoginVerifier) GoString() string
GoString — заглушка и для `%#v`.
func (LoginVerifier) IsZero ¶ added in v0.5.0
func (v LoginVerifier) IsZero() bool
IsZero — материала нет.
func (LoginVerifier) LogValue ¶ added in v0.5.0
func (v LoginVerifier) LogValue() slog.Value
LogValue — заглушка для журнала.
func (LoginVerifier) MarshalJSON ¶ added in v0.5.0
func (v LoginVerifier) MarshalJSON() ([]byte, error)
MarshalJSON — отказ.
func (LoginVerifier) MarshalText ¶ added in v0.5.0
func (v LoginVerifier) MarshalText() ([]byte, error)
MarshalText — отказ.
func (LoginVerifier) Reveal ¶ added in v0.5.0
func (v LoginVerifier) Reveal() string
Reveal — ЕДИНСТВЕННЫЙ выход материала. Звать его вправе только места, перечисленные гейтом `TestLoginVerifierStaysInside`.
func (LoginVerifier) SameMaterial ¶ added in v0.5.0
func (v LoginVerifier) SameMaterial(other LoginVerifier) bool
SameMaterial — тот же ли материал у двух значений, сверкой постоянного времени и без выхода материала наружу (kaname#458, Р5): выдача входа сверяет материал, проверенный до транзакции, с хранимым после захвата строки личности. Нулевое значение не совпадает ни с чем, и с нулевым тоже: «материала нет» — не материал.
func (LoginVerifier) String ¶ added in v0.5.0
func (v LoginVerifier) String() string
String — заглушка.
type Membership ¶
type Membership struct {
ID MembershipID
AccountID AccountID
// AccountName — ЗЕРКАЛО имени аккаунта, источник истины сам аккаунт.
// Заполняется на чтении соединением в той же БД; пустое значение означает
// «имя не задано», а НЕ «аккаунта нет».
AccountName AccountName
UserID UserID
State MembershipState
// InvitedBy — кто пригласил. Это и есть след приглашения, ПЕРЕЖИВАЮЩИЙ вход:
// активация трогает состояние и отметку правки и не трогает это поле.
// Пусто у членства, заведённого не приглашением.
InvitedBy UserID
// CreatedAt — когда членство заведено; для приглашения — когда оно выписано.
CreatedAt time.Time
// UpdatedAt — когда состояние менялось в последний раз.
UpdatedAt time.Time
}
Membership — принадлежность человека аккаунту как ОТДЕЛЬНАЯ связь.
Ресурс ВЫХОДНОЙ: глаголов создания, правки и снятия у него на читающей поверхности нет. Заводит членство приглашение, снимает — исключение из аккаунта, и оба принадлежат потоку человека.
type MembershipID ¶
type MembershipID string
MembershipID — идентификатор членства, дефис-форма `mbr-<17>`.
Он ВЫЧИСЛЯЕТСЯ из пары «человек × аккаунт» неизменяемой SQL-функцией без соли, а не чеканится: писателей строки членства больше одного, и все обязаны прийти к ОДНОЙ строке на одну пару. Отсюда два следствия, которые здесь названы, потому что они меняют решения читающего кода:
- идентификатор ПЕРЕИСПОЛЬЗУЕТСЯ. Уникальность пары полная, снятие есть удаление строки, функция неизменяема — значит повторное приглашение того же человека в тот же аккаунт вернёт ТОТ ЖЕ идентификатор. Признак «строка заведена заново» — `CreatedAt`, а не `ID`;
- идентификатор ВЫЧИСЛИМ ПОСТОРОННИМ. Зная человека и аккаунт, его получает кто угодно, ни разу не обратившись к платформе. Поэтому одиночное чтение обязано быть суженным по аккаунту в САМОМ ЗАПРОСЕ: без этого адрес чужого членства строится арифметикой, а не перебором.
type MembershipState ¶
type MembershipState string
MembershipState — состояние членства. Значений ровно два, и словарь закреплён CHECK'ом на таблице: третьего в колонке не появится.
Согласованность состояния членства со статусом человека конструкцией НЕ закреплена — её держат писатели (зеркалящий триггер строки человека и два стейтмента репозитория). Читающий код поэтому судит то, что в строке лежит, и не выводит одно из другого.
const ( // MembershipStatePending — человек приглашён и ещё не входил. // // Наблюдаемо АККАУНТ-СКОУПНЫМ чтением и не наблюдаемо своим списком: у // приглашённого нет внешнего субъекта, значит нечем аутентифицироваться, // значит своего чтения он вызвать не может. Это не расхождение проекций, а // арифметика — разные наблюдатели видят разное. MembershipStatePending MembershipState = "PENDING" // MembershipStateActive — человек состоит в аккаунте. MembershipStateActive MembershipState = "ACTIVE" )
type MembershipTuple ¶
type MembershipTuple struct {
User string // e.g. "user:usr-…", "group:grp-…#member"
Relation string // tier relation (e.g. "editor")
Object string // e.g. "compute_instance:inst-1"
}
MembershipTuple — a per-object FGA relation tuple the reconciler emits/revokes for a materialized membership (subject → tier → object). A flat value so the reconcile use-case stays transport/storage-agnostic; the pg adapter maps it to the fga_outbox payload.
type MirrorObject ¶
type MirrorObject struct {
ObjectType string
ObjectID string
ParentProjectID string
// ParentAccountIDs — АККАУНТЫ, под которыми лежит объект. Множественное
// число здесь не запас на будущее: у ЛИЧНОСТИ принадлежность аккаунту
// перестала быть свойством её строки и стала отдельной связью, которых у
// человека бывает несколько (kacho#470/#471), поэтому скалярное поле называло бы
// ОДИН аккаунт из многих — и выдача второго аккаунта не находила бы своего
// человека by construction (kacho#1172).
//
// У всех прочих типов набор вырожден: ноль элементов (объект не лежит ни в
// одном аккаунте) либо один. Пустые строки в набор НЕ КЛАДУТСЯ — «аккаунта
// нет» выражается отсутствием элемента, а не элементом-пустышкой, иначе
// область с пустым идентификатором совпала бы с ним.
ParentAccountIDs []string
Labels map[string]string
}
MirrorObject — the same-DB parent-scope projection of an owner object read from resource_mirror (β fed it; γ reads it). Pure value; the reader adapter fills it.
func (MirrorObject) IsContainedIn ¶
func (m MirrorObject) IsContainedIn(scope ScopeAnchor) bool
IsContainedIn reports whether a mirror object lies UNDER the given scope-anchor (the single containment predicate for byName AND byLabel — parity).
project:P ⊑ project:P (same project) project:P ⊑ account:A if A ∈ mirror.ParentAccountIDs any ⊑ cluster:* (cluster contains everything)
A cluster-scoped binding contains every registered object. The cluster id is not compared (there is a single cluster root in the FGA model).
This predicate is PURE (no DB): it trusts ParentAccountIDs to already carry the object's FULL account set. For a mirror-fed object registered with only its owning PROJECT, the reader adapter resolves the account through the project→account hierarchy same-DB (resource_mirror reader COALESCE) BEFORE filling ParentAccountIDs, so an account-scoped binding transitively contains an object nested in a project of its account even when the stored parent_account_id column was empty. The resolution is account-bounded (one project → one account), so this predicate never leaks across the account boundary.
ЧЛЕНСТВО ЗДЕСЬ — «хотя бы одно», и это не послабление. У личности аккаунтов столько, сколько у человека членств (kacho#1172): выдача аккаунта B накрывает его потому, что он состоит в B, и НИ ОДИН чужой аккаунт от этого его не накрывает — набор перечисляет ровно те аккаунты, связь с которыми существует строкой `kaname.memberships`.
Пустая область (scope.ID == "") не совпадает ни с чем: набор пустых строк не содержит by construction (см. комментарий поля), но проверка стоит здесь же — сравнение «пусто с пусто» иначе давало бы истину на объекте без аккаунта.
func (MirrorObject) MatchesLabels ¶
func (m MirrorObject) MatchesLabels(matchLabels map[string]string) bool
MatchesLabels reports whether the mirror object's labels satisfy the AND-equality match set: for EVERY (k,v) in matchLabels the object must have labels[k]==v (superset allowed — the object may carry extra labels). This is the in-Go equivalent of the JSONB `labels @> matchLabels` probe used by the reader's SQL filter; kept here so the containment verdict is testable in pure domain and the reconciler can re-assert it on a candidate set.
type ModuleSet ¶
type ModuleSet interface {
// IsKnownModule — состоит ли модуль в наборе.
IsKnownModule(module string) bool
}
ModuleSet — членство в наборе модулей платформы, каким его знает ВЫЗЫВАЮЩИЙ.
Подстановочный знак `*` членом набора не является ни в одной реализации: он не имя модуля, а маркер политики, и разбирается Rule.Validate отдельно.
func ModuleSetOf ¶
ModuleSetOf — набор из перечня имён, для вызывающего, у которого перечень уже в руках: канон дерева, фикстура пробы, применитель ролей модуля.
Пустой перечень даёт набор, не признающий НИЧЕГО, и это не вырожденный случай, а тот же fail-closed, что у отсутствующего набора ниже: «перечень пуст» не есть «принимаем любой». Разница лишь в том, что здесь отказ приходит с именем модуля, а там — с указанием на непровязанный источник.
type OAuthClientName ¶
type OAuthClientName string
OAuthClientName — человекочитаемое имя токена (SA-key / user-token). Судится единственной формой имени дерева, как и остальные имена iam.
Пустая строка остаётся законным ВХОДОМ выпуска и означает «назови сам»: до записи её заменяет имя, производное от идентификатора (`validate.NameOrDefault` в use-case). Здесь пустое уже не проходит — эта проверка судит то, что БУДЕТ ЗАПИСАНО, а записи с пустым именем не бывает.
func (OAuthClientName) Validate ¶
func (n OAuthClientName) Validate() error
Validate — OAuthClientName судится той же формой, что и остальные имена.
Прежде здесь стояла ветка «пустое допустимо», и она была единственным местом iam, где имя доживало до записи пустым. Пустое имя — не «имя, которого нет», а ресурс, который не ищется, не отличается в списке и показывается прочерком. Ветка снята: пустое заменяется умолчанием ДО записи, в use-case выпуска.
type OperationID ¶
type OperationID string
type PasswordCostClass ¶ added in v0.5.0
type PasswordCostClass struct {
Format PasswordHashFormat
Params map[PasswordHashCostParam]uint32
}
PasswordCostClass — формат и полный набор параметров стоимости этого формата.
func (PasswordCostClass) Key ¶ added in v0.5.0
func (c PasswordCostClass) Key() string
Key — ключ класса: формат и параметры в порядке, объявленном перечнем (`2a cost=12`, `argon2id memory=65536 iterations=3 parallelism=4`). Детерминирован: одна пара «формат, параметры» — один ключ, каким бы порядком ни обходилась карта.
func (PasswordCostClass) ParamsLabel ¶ added in v0.5.0
func (c PasswordCostClass) ParamsLabel() string
ParamsLabel — параметры одной строкой через запятую (`cost=12`, `memory=65536,iterations=3,parallelism=4`): значение метки наблюдаемости.
func (PasswordCostClass) Validate ¶ added in v0.5.0
func (c PasswordCostClass) Validate() error
Validate — формат из перечня; названы ВСЕ параметры стоимости формата и ни одного чужого. Неназванный параметр читался бы нулём, а класс с нулём в стоимости — не класс.
type PasswordHashCostParam ¶ added in v0.5.0
type PasswordHashCostParam string
PasswordHashCostParam — параметр СТОИМОСТИ формата: тот, что назначает цену одной проверки. Соль и длина ключа параметрами стоимости не являются и потолка не несут — они не меняют ни времени, ни памяти проверки.
const ( // CostParamBcryptCost — показатель стоимости bcrypt: время растёт вдвое на // каждую единицу, память от него не зависит вовсе. CostParamBcryptCost PasswordHashCostParam = "cost" // CostParamArgon2Memory — память argon2id в КиБ: она же и есть память // одной проверки, которую страж старта сверяет с пределом процесса. CostParamArgon2Memory PasswordHashCostParam = "memory" // CostParamArgon2Iterations — число проходов argon2id. CostParamArgon2Iterations PasswordHashCostParam = "iterations" // CostParamArgon2Parallelism — степень параллельности argon2id. CostParamArgon2Parallelism PasswordHashCostParam = "parallelism" )
type PasswordHashFormat ¶ added in v0.5.0
type PasswordHashFormat string
PasswordHashFormat — признак формата проверочного значения: первый сегмент его разметки, как он лежит в самом значении.
const ( // PasswordHashFormatBcrypt — НАСЛЕДУЕМЫЙ формат: значения, заведённые // прежним поставщиком личности. Продукт их читает и не пишет. // // Признак — `2a`. Соседние написания bcrypt (`2b`, `2y`) в перечень НЕ // внесены: популяция их не несёт, а запись без предмета обещала бы // возможность, которой нет. Встретится такое значение — его назовёт // перепись переноса, громко и с числом строк, а не примет молча. PasswordHashFormatBcrypt PasswordHashFormat = "2a" // PasswordHashFormatArgon2id — ОБЪЯВЛЕННЫЙ и растущий формат: им заводится // каждое новое значение. PasswordHashFormatArgon2id PasswordHashFormat = "argon2id" )
func (PasswordHashFormat) Admissibility ¶ added in v0.5.0
func (f PasswordHashFormat) Admissibility(p PasswordHashCostParam) (PasswordHashParamRange, bool)
Admissibility — область допустимости параметра у этого формата.
func (PasswordHashFormat) CostParams ¶ added in v0.5.0
func (f PasswordHashFormat) CostParams() []PasswordHashCostParam
CostParams — параметры стоимости формата, в устойчивом порядке.
type PasswordHashFormatRecord ¶ added in v0.5.0
type PasswordHashFormatRecord struct {
// Format — признак формата.
Format PasswordHashFormat
// Writable — пишет ли продукт значения этого формата.
Writable bool
// Ceiling — потолок по каждому параметру стоимости формата.
Ceiling map[PasswordHashCostParam]uint32
// Floor — пол; ТОЛЬКО у записываемой записи.
Floor map[PasswordHashCostParam]uint32
// Why — чем выбран потолок. Потолок выводится из ЁМКОСТИ, а не из переписи
// одной установки, и причина обязана стоять рядом с числом.
Why string
}
PasswordHashFormatRecord — запись перечня.
func PasswordHashFormatByMarker ¶ added in v0.5.0
func PasswordHashFormatByMarker(marker string) (PasswordHashFormatRecord, bool)
PasswordHashFormatByMarker — запись по признаку формата.
Второго исхода, кроме «нашлась» и «такой записи нет», у поиска НЕТ: корзины «прочее» перечень не имеет. Признак вне перечня — наша ошибка данных либо настройки, а не человека и не временный сбой соседа, и отвечать за него обязан отдельный ГРОМКИЙ исход проверяющего, а не запись-заглушка.
func PasswordHashFormats ¶ added in v0.5.0
func PasswordHashFormats() []PasswordHashFormatRecord
PasswordHashFormats — перечень целиком, КОПИЕЙ: вызывающий не может его расширить, а расширенный на месте перечень перестал бы быть закрытым.
func (PasswordHashFormatRecord) MemoryPerVerificationAtCeilingBytes ¶ added in v0.5.0
func (r PasswordHashFormatRecord) MemoryPerVerificationAtCeilingBytes() uint64
MemoryPerVerificationAtCeilingBytes — память, которую занимает ОДНА проверка значения на потолке этой записи. Это та величина, которую страж старта умножает на ёмкость и сверяет с пределом памяти процесса (PWV-15, 15.7).
ВЫВОДИТСЯ из потолка, а не выписывается рядом с ним: выписанная, она стала бы вторым местом об одном предмете и разошлась бы с потолком при первой же его правке — молча, потому что расхождение не наблюдаемо ничем.
func (PasswordHashFormatRecord) Validate ¶ added in v0.5.0
func (r PasswordHashFormatRecord) Validate() error
Validate — запись, годная к объявлению. Зовётся пробой перечня и гейтом дерева; самопроверяющаяся запись не даёт перечню разойтись с нормой молча.
type PasswordHashParamRange ¶ added in v0.5.0
type PasswordHashParamRange struct {
// Min — наименьшее значение, которое читатель считает без искажения.
Min uint32
// Max — наибольшее. Где строже спецификация, берётся она; где строже
// читатель — он.
Max uint32
// Why — чем граница обоснована. Запись без причины неотличима от записи
// без предмета.
Why string
}
PasswordHashParamRange — область ДОПУСТИМОСТИ параметра: пересечение того, что допускает спецификация формата, и того, что читатель исполняет без искажения. Обе границы включительны.
type Permission ¶
type Permission string
func (Permission) Validate ¶
func (p Permission) Validate() error
Validate — Permission: one element with wildcard semantics.
type Permissions ¶
type Permissions []Permission
func CompileRules ¶
func CompileRules(rules []Rule) (Permissions, error)
CompileRules deterministically compiles authored rules into the INTERNAL compiled permission set (4-segment `module.resource.resourceName.verb`), for FGA emission/Check-reuse. Pure + deterministic:
- ARM_ANCHOR → `m.r.*.v` for each (resource, verb) over the rule's module.
- ARM_NAMES → `m.r.<id>.v` for each (resource, id, verb) over the module.
- ARM_LABELS → NOT compiled (excluded).
- verb `*` → projection keeps the `*` verb segment (`m.r.*.*` / `m.r.<id>.*`), it does NOT expand the closed per-verb set (that is the FGA-emit concern). The projection holds `*`.
The result is de-duplicated (order-preserving over first occurrence) and capped at MaxCompiledPermissions; exceeding the cap → INVALID_ARGUMENT "compiled permissions exceed 1024" (not silent truncation, not INTERNAL).
The projection is system-context-independent (a system role's `*`-segments are authored verbatim into the rules and projected as-is), so CompileRules takes no systemCtx flag — the wildcard/system policy is enforced upstream by Rule.Validate.
func (Permissions) Validate ¶
func (p Permissions) Validate() error
Validate — Permissions: cardinality 1..1024; each — Permission.Validate.
The ≥1 lower bound applies to the LEGACY permissions-only role path (a role authored as a bare permission set must carry at least one). Cap raised 256→1024 in lockstep with the DB CHECK iam_permissions_valid (migration 0025) and the proto (size) bound, so the compiled-permission set derived from a role's rules (CompileRules, ≤MaxCompiledPermissions) always passes domain + DB validation (acceptance R-12 / A-12). A rules-role uses ValidateCompiled (no lower bound) — a label-only role compiles to an EMPTY set by design (ARM_LABELS excluded).
func (Permissions) ValidateCompiled ¶
func (p Permissions) ValidateCompiled() error
ValidateCompiled validates the INTERNAL compiled-permission projection of a rules-role: the 4-segment grammar parity + the ≤1024 cap, but WITHOUT the ≥1 lower bound. A rules-role whose rules are ALL ARM_LABELS compiles to an EMPTY permission set (matchLabels is not compiled — R-7 / fix #8), which is valid; the authority lives in rules[], not permissions[]. Only the legacy permissions-only path keeps the ≥1 floor (Validate). Acceptance A-10 (label-only positive).
type PrivilegeDerivation ¶
type PrivilegeDerivation string
PrivilegeDerivation — how a subject obtained a privilege.
const ( // DerivationDirect — the binding's subject IS the requested subject. Also the // zero value's meaning (see SubjectPrivilege.Derivation). DerivationDirect PrivilegeDerivation = "" // DerivationGroup — the binding's subject is a GROUP the requested subject is // a member of. Groups do not nest, so this is exactly one hop. DerivationGroup PrivilegeDerivation = "GROUP" )
type Project ¶
type Project struct {
ID ProjectID
AccountID AccountID
Name ProjectName
Description Description
Labels Labels
CreatedAt time.Time
}
Project — child Account-а («folder» в YC-стилистике, но без промежуточного Cloud). Уникальное имя per-Account (DB UNIQUE projects_account_name_unique).
FK: account_id → accounts(id) ON DELETE RESTRICT. account_id — hard-immutable после Create (Update его reject'ит).
func (Project) StructuralFacts ¶
func (p Project) StructuralFacts() []StructuralTuple
StructuralFacts returns the project's cluster and account pointers — both of which `project.super_admin: admin from account or any_admin from cluster` reads.
type ProjectName ¶
type ProjectName string
func (ProjectName) Validate ¶
func (n ProjectName) Validate() error
type PrunedSelectorType ¶
type PrunedSelectorType struct {
// ObjectType — точечный тип объекта, чью строку каталога сняли.
ObjectType string
// Outcome — что стало со строкой отбора, из которой тип вырезан.
Outcome SelectorPruneOutcome
// Reason — причина снятия строки каталога, записанная применителем
// манифеста. Причина ПЛАТФОРМЫ, а не действие арендатора: своё правило он не
// менял. ПУСТАЯ строка означает «строка каталога причины не несла» (снята
// ранней миграцией), а не «причину потеряли».
Reason string
// PrunedAt — момент вырезания. Время ТРАНЗАКЦИИ применения: у всего, что
// вырезано одним применением, он совпадает дословно.
PrunedAt time.Time
// AppliedBy — АВТОР применения, вырезавшего этот тип (kacho#2005). Та же величина
// и тот же источник, что у соседа: вопрос «кто снял» у обеих ведомостей
// общий — арендатор не различает, какой из проекций правила он лишился.
//
// ПУСТАЯ строка означает «строка вырезана ДО заведения колонки».
AppliedBy string
}
PrunedSelectorType — точечный тип, ВЫРЕЗАННЫЙ применителем каталога из отбора правила роли при снятии строки ресурса платформы (kacho#1988).
Чем это отличается от соседней [WithdrawnGrant], и почему они не сложены ¶
Переселение адресуется парой «тип объекта + глагол» и означает «право по этой паре действовало и перестало». Вырезание глагола не имеет ВООБЩЕ: отбор правила называет типы, а не пары, — поэтому у него своя ведомость и свой тип здесь. Сложить их значило бы дать вырезанному пустой глагол, а пустой глагол в соседе есть ЯКОРЬ объявления правила, то есть уже занятое значение.
Отпечаток правила сюда НЕ ПЕРЕЕЗЖАЕТ ¶
Ведомость ключуется тройкой «роль + отпечаток правила + тип»: отпечаток нужен ХРАНЕНИЮ, чтобы два правила, потерявшие один тип, не схлопнулись в одну строку. Читающему он не нужен и не отдаётся — это содержательный хеш, которого нет ни в одном контракте платформы; выставь мы его, способ хеширования стал бы частью контракта, а арендатор своё правило знает содержанием, а не дайджестом.
Следствие названо прямо: строки, различавшиеся ТОЛЬКО отпечатком, для читающего есть ОДИН факт и произносятся один раз.
type PublicReadContainer ¶ added in v0.5.0
type PublicReadContainer struct {
// Type — точечный тип, о котором ответ (kaname#874): по нему ворота находят
// вложенные правила, стоящие под этим типом.
Type string
// Declared — тип объявлен контейнером публичного чтения.
Declared bool
// ChildTypes — точечные типы живых строк каталога, чей указатель называет
// этот тип, отсортированно. Пусто — детей у типа нет либо каталог их не знает.
ChildTypes []string
}
PublicReadContainer — что живой каталог говорит о типе цели выдачи классу.
func (PublicReadContainer) CoveredChild ¶ added in v0.5.0
func (c PublicReadContainer) CoveredChild(rules Rules) (string, bool)
CoveredChild — первый (в порядке ChildTypes) тип-ребёнок, чтение которого роль открывает ЦЕЛИКОМ (Rule.ReadsWholeType); ok=false — такого ребёнка нет.
Читаются ОБЕ формы (kaname#874, M1 круга 1): правило верхнего уровня над ребёнком (исход SC-65 соседа не меняется) и вложенное правило под правилом над ЭТИМ типом (ND-05 (в)). Прежде ворота читали только верхний уровень, и роль, открывающая детей вложенным правилом, проходила на необъявленный контейнер молча.
type PublishedKey ¶
type PublishedKey struct {
KID KeyID
Algorithm SigningAlgorithm
PublicKeyPEM string
}
PublishedKey — ПУБЛИКУЕМАЯ форма ключа.
Поля приватной половины у этого типа НЕТ и быть не может (F1-05): положить её сюда не выражается, а не «запрещено правилом».
type RecoveryCode ¶ added in v0.5.0
type RecoveryCode struct {
ID RecoveryCodeID
UserID UserID
Digest CodeDigest
// IssuedAt — момент чеканки (часы полосы).
IssuedAt time.Time
// ExpiresAt — абсолютный срок, ОДИН столбец; величина — настройка.
ExpiresAt time.Time
// ConsumedAt — момент применения; nil — не применён. Ставит его один
// оператор базы (Ф5-05).
ConsumedAt *time.Time
}
RecoveryCode — запись кода (Р1). Состав закрыт: личность, свёртка, момент выдачи, срок, момент применения.
func (RecoveryCode) Expired ¶ added in v0.5.0
func (c RecoveryCode) Expired(now time.Time) bool
Expired — истёк ли срок на данный момент. Граница включающая: в сам момент срока кода уже нет.
func (RecoveryCode) Validate ¶ added in v0.5.0
func (c RecoveryCode) Validate() error
Validate — запись, годная к выдаче: судится до базы то, что база держит ограничениями.
type RecoveryCodeID ¶ added in v0.5.0
type RecoveryCodeID string
RecoveryCodeID — идентификатор ПОТОКА восстановления: строка кода и ключ идемпотентности журнала завершений (Р4). Наружу не адресуется.
type RecoveryCodeValue ¶ added in v0.5.0
type RecoveryCodeValue struct {
// contains filtered or unexported fields
}
RecoveryCodeValue — значение кода (Р1). Тип не печатается и не сериализуется; значение выходит ровно двумя методами: `Digest` (в хранилище) и `Letter` (в письмо).
func NewRecoveryCodeValue ¶ added in v0.5.0
func NewRecoveryCodeValue() (RecoveryCodeValue, error)
NewRecoveryCodeValue — свежий код из криптографического источника случайности: каждый знак — независимый выбор из алфавита.
func PresentedRecoveryCode ¶ added in v0.5.0
func PresentedRecoveryCode(value string) RecoveryCodeValue
PresentedRecoveryCode — код, как его прислал человек: регистр и разделители (пробелы, дефисы) не значимы, знаки-двойники приводятся к своим. Пустое после нормализации — отсутствие кода.
func (RecoveryCodeValue) Digest ¶ added in v0.5.0
func (v RecoveryCodeValue) Digest() CodeDigest
Digest — свёртка, по которой хранилище находит строку. Хранится и сравнивается только она.
func (RecoveryCodeValue) Format ¶ added in v0.5.0
func (v RecoveryCodeValue) Format(f fmt.State, verb rune)
Format — заглушка на любом глаголе форматирования.
func (RecoveryCodeValue) GoString ¶ added in v0.5.0
func (v RecoveryCodeValue) GoString() string
GoString — заглушка и для `%#v`.
func (RecoveryCodeValue) IsZero ¶ added in v0.5.0
func (v RecoveryCodeValue) IsZero() bool
IsZero — кода нет.
func (RecoveryCodeValue) Letter ¶ added in v0.5.0
func (v RecoveryCodeValue) Letter() string
Letter — форма для письма: две группы через дефис. Единственный выход к человеку.
func (RecoveryCodeValue) LogValue ¶ added in v0.5.0
func (v RecoveryCodeValue) LogValue() slog.Value
LogValue — заглушка для журнала.
func (RecoveryCodeValue) MarshalJSON ¶ added in v0.5.0
func (v RecoveryCodeValue) MarshalJSON() ([]byte, error)
MarshalJSON — отказ.
func (RecoveryCodeValue) MarshalText ¶ added in v0.5.0
func (v RecoveryCodeValue) MarshalText() ([]byte, error)
MarshalText — отказ.
func (RecoveryCodeValue) String ¶ added in v0.5.0
func (v RecoveryCodeValue) String() string
String — заглушка.
type RecoveryCompletion ¶
type RecoveryCompletion struct {
RecoveryJTI string
ExternalID ExternalSubject
UserID UserID
RevokedSessionCount int32
}
RecoveryCompletion — one row of the recovery-completed idempotency ledger. PK recovery_jti dedups at-least-once delivery. The row stores the deterministic primary user_id and the revoked session count so a duplicate delivery can replay the same Operation.metadata without re-running any side-effect.
Источника события ДВА (Ф5 Р4): обратный вызов поставщика личности называет внешнего субъекта; наш поток восстановления (Ф5, `kacho#1271`) называет человека его строкой, а внешнего субъекта не несёт — у личности, заведённой нашей регистрацией, его нет. Поэтому `ExternalID` необязателен; заданный по-прежнему ограничен длиной.
func (RecoveryCompletion) Validate ¶
func (r RecoveryCompletion) Validate() error
Validate — self-validating domain entity. Length bounds mirror the migration CHECK constraints + the proto field annotations.
type ResourceRef ¶
ResourceRef — пообъектный указатель {type,id} (redesign-2026 F8 / B1). В отличие от общего Referrer имени НЕ несёт: цель iam — строгая форма {type,id}. `Type` — точечный ключ `<module>.<resource>`.
type ResourceType ¶
type ResourceType string
ResourceType — ЯКОРЬ ОБЛАСТИ привязки: один из трёх ярусов иерархии (`cluster`/`account`/`project`). Вокабуляр объявлен ОДИН раз — `scopeAnchorTiers` в access_binding_scope.go, — и `Validate()` судит по нему. Пообъектный тип якорем не бывает: он живёт на оси `target` (F8).
func (ResourceType) Validate ¶
func (r ResourceType) Validate() error
Validate — ResourceType — ярус иерархии, и только он.
Судит ЕДИНСТВЕННОЕ объявление вокабуляра якоря (`scopeAnchorTiers`), а не своя копия: копия здесь была, разошлась с ним на 20 записей и принимала пообъектные типы, которых не производит ни один путь записи. Разбор — у самого объявления.
type RevocationReason ¶ added in v0.7.0
type RevocationReason string
RevocationReason — закрытый словарь причин отзыва выдачи (kaname#963, Д2). Написание — слово хранения (`access_bindings.revocation_reason`).
const ( // RevocationReasonNone — выдача не отозвана либо отозвана до заведения поля. RevocationReasonNone RevocationReason = "" // RevocationReasonRevoke — глагол `Revoke`. RevocationReasonRevoke RevocationReason = "REVOKE" // RevocationReasonExpired — истёк срок, отозвал проход срока. RevocationReasonExpired RevocationReason = "EXPIRED" // RevocationReasonResourceUnregistered — снята регистрация объекта цели. RevocationReasonResourceUnregistered RevocationReason = "RESOURCE_UNREGISTERED" // RevocationReasonDelegationRevoked — отозвано делегирование, по которому // выдача сделана (kaname#986, Р4). RevocationReasonDelegationRevoked RevocationReason = "DELEGATION_REVOKED" )
type Role ¶
type Role struct {
ID RoleID
ClusterID ClusterID // set for system role
AccountID AccountID // set for account-scoped custom
ProjectID ProjectID // set for project-scoped custom
Name RoleName
Description Description
// Rules — authored policy (RBAC rules-model 2026). Source of truth + public
// API surface. Compiled into Permissions (internal, FGA-emit) by CompileRules.
Rules Rules
// Permissions — INTERNAL compiled form (anchor/names arms; match_labels NOT
// compiled). Derived from Rules via CompileRules; NOT a public API field for
// rules-roles. Legacy permissions-only roles (no Rules) keep their stored set.
Permissions Permissions
IsSystem bool
// OwnerModule — модуль, которому роль принадлежит. Пусто у ПЛАТФОРМЕННОЙ
// роли (admin/edit/view/owner, system.*), непусто у роли, объявленной
// манифестом этого модуля.
//
// Носит ровно ОДИН смысл — владение, — и потому отделяет послабление
// подстановки от кластерного якоря: `IsSystem` продолжает означать «арендатор
// эту роль не правит» и больше не означает «этой роли можно подставлять
// звёздочку». Политика выводится из пары ОДНИМ местом — [PolicyOfRole].
//
// Наружу не проецируется: `Role` публичного контракта этой колонки не несёт
// (задача продукта kacho#1032, тип изменения — ВВОДЯЩЕЕ, без нового поля API).
OwnerModule string
CreatedAt time.Time
// CreatedByUserID — authoring principal (governance/audit). Optional.
CreatedByUserID UserID
// UpdatedAt — last-mutation timestamp. Zero until first Update.
UpdatedAt time.Time
// Labels — tenant-facing метки САМОГО ресурса Role (НЕ путать с
// Rule.MatchLabels, отбирающим объекты под грантом). Делают Role
// label-selectable наравне с account/project (ARM_LABELS-грант на iam.role →
// v_list по `labels @> matchLabels`; List фильтрует viewer ∪ v_list).
Labels Labels
// Integrity — целость роли: даёт ли она то, что объявляет (kacho#1035).
//
// ВЫВОДИТСЯ НА ЧТЕНИИ и НЕ ХРАНИТСЯ: колонки у неё нет, в перечень колонок
// писателя она не входит и [Role.Validate] её не судит. Нулевое значение
// означает «этим путём не вычислено» — так её и видят пути, которые роль не
// читают, а возвращают эхом мутации.
Integrity RoleIntegrity
// Lifecycle — ОБЪЯВЛЕНА роль манифестом модуля сегодня либо СНЯТА (kacho#1913).
//
// Отдельно от `Integrity` рядом, и различие несущее: `RoleHealthEmpty` даёт
// и снятая роль, и объявленная, чьи строки каталога сняты, — а следующий шаг
// у арендатора разный. Довод целиком — [RoleLifecycle].
//
// ПРИХОДИТ ЧТЕНИЕМ: колонки пометки у строки есть, но заполняют это поле
// `Get` и `List`, а ответ операции его не несёт — нулевое состояние там
// означает «этим ответом не вычислено», ровно как у соседа.
Lifecycle RoleLifecycle
// Withdrawn — ЧТО у роли отобрано и почему (kacho#1992): строки ведомости
// переселения. ОБЪЯСНЯЕТ состояние целости и не определяет его — у роли,
// пострадавшей третьим путём, переселения не было вовсе, и список пуст при
// нездоровом состоянии.
//
// Пустой срез означает «отобранного нет», а не «не читали»: разводит их
// [RoleIntegrity.Health] рядом, у которого нулевое состояние есть «этим
// ответом не вычислено».
Withdrawn []WithdrawnGrant
// PrunedSelectorTypes — какие точечные типы ВЫРЕЗАНЫ из отбора правил этой
// роли и почему (kacho#1988): строки ведомости вырезания.
//
// Сосед [Role.Withdrawn] отвечает про ДВЕ проекции правила, у которых есть
// пара «тип + глагол»; эта — про ТРЕТЬЮ, где глагола нет вовсе. Разные
// ведомости, разные события, поэтому два поля, а не одно.
//
// Пустой срез означает «вырезанного нет», а не «не читали»: разводит их
// [RoleIntegrity.Health] рядом, у которого нулевое состояние есть «этим
// ответом не вычислено».
PrunedSelectorTypes []PrunedSelectorType
// RuleStates — состояние КАЖДОГО правила роли (kacho#1962): действует, отозвано
// платформой либо не разрешилось. Записей ровно по числу правил.
//
// Отвечает на вопрос, которого нет ни у `Integrity`, ни у `Withdrawn`: КАКОЕ
// правило пострадало и по КАКОЙ ИЗ ДВУХ причин. Картина счётчиков у причин
// ОДИНАКОВА, а следующий шаг арендатора — разный.
//
// ПРИХОДИТ ЧТЕНИЕМ, НЕ ХРАНИТСЯ — тем же доводом, что `Integrity` рядом.
// ПУСТОЙ СРЕЗ означает «этим путём не вычислено» и законен: ответ операции
// состояния не несёт. У роли БЕЗ правил он пуст и на чтении — состояние есть
// свойство правила, и у роли без правил его нет.
RuleStates []RuleState
// TypeVerbs — набор глаголов ТИПА, каким его объявляет ЖИВАЯ строка каталога
// (kacho#1994). Тем же набором идёт материализация, поэтому превью и эмиссия не
// могут разойтись.
//
// ПРИХОДИТ ПРОВЯЗКОЙ, НЕ ХРАНИТСЯ — как и `Integrity` рядом: колонки у неё
// нет, писатель её не пишет, [Role.Validate] её не судит. Отличие от
// `Integrity` в том, чем является НУЛЕВОЕ значение: там оно означает «этим
// путём не вычислено» и законно, здесь — ОТКАЗ проекции. Показ по словарю,
// порождённому сборкой, неотличим от честного превью, поэтому «нечем
// ответить» обязано быть отказом, а не тихим запасным путём.
TypeVerbs TypeVerbLookup
// HeldNames — НЕ ХРАНИТСЯ. Ключи объектов, названные в `resourceNames`
// правил, которые на приёме роли были зарегистрированы (Р8 приёмки
// `unregistration-revokes-its-grants.md`). Запись роли берёт на каждом замок
// строки регистрации и отказывает, если строки уже нет: роль, принятая до
// снятия и записанная после, снятого ключа не называет (URG-26).
HeldNames []RuleNameHold
}
Role — multi-scope. Exactly one scope field is non-NULL:
- is_system=true + ClusterID set: system role (`system.admin`, ...).
- is_system=false + AccountID set: account-scoped custom role.
- is_system=false + ProjectID set: project-scoped custom role.
Enforced by DB CHECK `roles_definition_tier_xor` + a partial UNIQUE per scope. Domain.Validate duplicates the CHECK to give friendly errors before reaching the DB. (The legacy B2B-tenant role scope was removed; a custom role is scoped to exactly one of {account, project}.)
func (Role) AuthoredVerbs ¶
func (r Role) AuthoredVerbs(lookup TypeVerbLookup) []string
AuthoredVerbs is the deduped, canonically-ordered union of the role's rule verbs (`*` expands to the verb set of the addressed type). Empty for a label-only / rules-less role.
func (Role) CanonicalRank ¶
CanonicalRank returns the ordering rank of a canonical system role (0=viewer … 3=owner) so the catalog can present the four first among system roles; a non-canonical role sorts after them.
func (Role) DefinitionTierID ¶
DefinitionTierID returns the anchor object id of the role's definition tier (cluster / account / project id), matching DefinitionTierType.
func (Role) DefinitionTierType ¶
DefinitionTierType returns the dotted definition-tier type of the role: iam.cluster for a system role (cluster_id set), iam.account / iam.project for a custom role. Empty when no anchor is set (should not happen under the XOR CHECK).
func (Role) DisplayName ¶
DisplayName is the friendly catalog label. For a canonical system role it is the curated name (Viewer/Editor/Admin/Owner); otherwise it defaults to the role name.
func (Role) EffectiveVerbs ¶
func (r Role) EffectiveVerbs(lookup TypeVerbLookup) []string
EffectiveVerbs is AuthoredVerbs plus the editor `delete*` qualifier for an editor-tier role.
func (Role) GrantsReadsOnly ¶ added in v0.5.0
GrantsReadsOnly — каждый глагол роли — `get` либо `list` (Р4, SC-21).
Судится АВТОРСКОЕ написание, а не раскрытый набор: глагол `*` раскрывается в набор ТИПА, который растёт вместе с каталогом, и роль, читающая сегодня, завтра писала бы — поэтому `*` пишущим считается всегда. Роль без правил судится по своей хранимой проекции прав (`<модуль>.<ресурс>.<…>.<глагол>`), и в ней действует то же правило.
То же правило держит база (`kaname.role_grants_reads_only`) — на пути записи, где у выдачи и правки роли последнее слово (ban #10); здесь оно ради синхронного отказа до заведения операции.
func (Role) IsClusterAdminRole ¶
IsClusterAdminRole reports whether r is THE system cluster-admin superuser role.
It is identified by its PINNED deterministic id (ClusterAdminRoleID `admin` or SystemAdminRoleID `system.admin`) AND is_system AND (defence-in-depth) that it still carries the full `*.*.*` ARM_ANCHOR rule (module:*, `*` resource, `*` verb). Matching by id — NOT by the bare `*.*.*` shape — is load-bearing for #8: the `owner` system role (OwnerRoleID, migration 0035) carries the SAME `*.*.*` shape, so a shape-only predicate would misclassify owner as cluster-admin and let an owner@GLOBAL+all binding slip past the A-05 reject. owner is auto-bound at ACCOUNT scope only and is NOT the GLOBAL+all exception.
This is the ONLY role for which a GLOBAL+all binding is legal (A-05c) — its binding is materialized as the D-9 cluster-relation, not per-object.
func (Role) IsSystemDerived ¶
IsSystemDerived reports whether the role is a system role, DERIVED from its definition tier (tierType == iam.cluster) rather than a stored provenance flag (redesign-2026 F4). Equivalent to ClusterID != "".
func (Role) Purpose ¶
Purpose is the one-line description of a canonical system role; empty otherwise.
func (Role) Validate ¶
Validate — multi-scope XOR formula + rules/permissions.
A role is valid when EITHER it carries authored Rules (the rules-model 2026 authority) OR a legacy compiled Permissions set (back-compat read of pre-rules roles).
When Rules is set (a rules-role) it is validated through Rules.Validate with the policy derived from the row (PolicyOfRole of IsSystem + OwnerModule) and the compiled Permissions projection is validated for the 4-seg grammar + cap ONLY — NOT the ≥1 lower bound (ValidateCompiled): a label-only role (all rules ARM_LABELS) compiles to an EMPTY permission set by design and must be accepted. The ≥1 floor is retained for the LEGACY permissions-only path (no Rules) so a degenerate legacy role with an empty set cannot exist. `modules` — набор модулей платформы, каким его знает вызывающий: правило роли называет модуль, а домен закрытого набора не объявляет (module_set.go).
func (Role) VerbNotes ¶
func (r Role) VerbNotes(lookup TypeVerbLookup) map[string]string
VerbNotes returns the per-verb clarifications for the effective preview. Only the editor `delete*` qualifier carries a note today.
func (Role) WithoutComputedState ¶
WithoutComputedState — копия роли без вычисленного состояния.
Приёмник — ЗНАЧЕНИЕ, и это не стиль: вызывающий переводит роль в ответ операции и продолжает пользоваться своей, поэтому проекция обязана быть неразрушающей.
TypeVerbs НЕ обнуляется намеренно: у него нулевое значение означает не «не вычислено», а ОТКАЗ проекции (см. комментарий поля), и путь мутации его заполняет сам — превью глаголов роль несёт и в ответе операции.
type RoleHealth ¶
type RoleHealth uint8
RoleHealth — состояние целости роли.
Нулевой вариант несёт ЕДИНСТВЕННЫЙ смысл: «этим ответом не вычислено». Он не является ни одним из трёх исходов и HealthOf его не возвращает никогда — иначе «посчитано, терять нечего» стало бы неотличимо от «не считали».
const ( // RoleHealthUnknown — не вычислено ЭТИМ ответом. НИКОГДА не «здорова». RoleHealthUnknown RoleHealth = iota // RoleHealthHealthy — каждый адресуемый сегмент имеет строку проекции. RoleHealthHealthy // RoleHealthDegraded — часть сегментов проекции не даёт, часть даёт. RoleHealthDegraded // RoleHealthEmpty — сегменты объявлены, проекции нет НИ ОДНОЙ. RoleHealthEmpty )
type RoleID ¶
type RoleID string
func SystemRoleID ¶
SystemRoleID — идентификатор системной роли по её ИМЕНИ.
Имя роли берётся ДОСЛОВНО: приведение имени к другому написанию перед хешированием (змеиное к камельному и обратно) даёт другой идентификатор и разрывает выданные права. Имя роли и имя ресурса — разные словари, и применитель их не сводит (приёмка §3.7).
type RoleIntegrity ¶
type RoleIntegrity struct {
Health RoleHealth
// Declared — сколько адресуемых сегментов объявляют правила роли.
Declared int
// Unresolved — сколько из них не дают ни одной строки проекции.
Unresolved int
}
RoleIntegrity — состояние целости и пара величин, из которых оно выведено.
Счётчики без состояния были бы двусмысленны: ноль есть И законная величина (роль без адресуемых сегментов), И признак «не считали». Разводит их RoleHealth: у вычисленной целости состояние непустое всегда.
func HealthOf ¶
func HealthOf(declared, unresolved int) RoleIntegrity
HealthOf выводит состояние из пары величин. ЕДИНСТВЕННОЕ место, где решается, какое состояние несёт роль.
Роль без адресуемых сегментов (подстановка в модуле и ресурсе — форма `*.*` администратора кластера, либо унаследованная роль без правил) читается ЗДОРОВОЙ, а не пустой: терять ей нечего, и тревога на ней была бы ложной. Прибор, чьи находки ложны, перестают читать — вместе с настоящими находками.
type RoleLifecycle ¶
type RoleLifecycle struct {
State RoleLifecycleState
// RetiredAt — момент снятия. Нулевое время у объявленной роли.
RetiredAt time.Time
// RetiredReason — причина снятия. Без неё «отобрали» неотличимо от
// «сломалось».
RetiredReason string
// RetiredBy — кто снял. Сегодня производитель один — путь старта, и он
// называет процессного актора; глагол применения придёт с `kacho#1034` и назовёт
// проверенную личность вызывающего. Пустая строка у снятой роли означает
// «помечено до заведения колонки», а не «автора потеряли».
RetiredBy string
}
RoleLifecycle — состояние роли и обстоятельства её снятия.
Три сопутствующие величины непусты ТОЛЬКО у снятой роли, и это свойство СХЕМЫ, а не кода: `roles_live_matches_retired` делает состояние «снята и жива» неконструируемым.
func (RoleLifecycle) Withdrawn ¶
func (l RoleLifecycle) Withdrawn() bool
Withdrawn — снята ли роль. Отдельный вопрос от `State != Declared`: у невычисленного состояния ответ «нет», и он верен — утверждать снятие по молчанию нельзя.
type RoleLifecycleState ¶
type RoleLifecycleState uint8
RoleLifecycleState — объявлена роль манифестом либо снята.
Нулевой вариант несёт ЕДИНСТВЕННЫЙ смысл: «этим ответом не вычислено». Он не является ни одним из двух исходов, и путь чтения его не возвращает никогда — иначе «прочитано, роль объявлена» стало бы неотличимо от «не читали».
const ( // RoleLifecycleUnknown — не вычислено ЭТИМ ответом. НИКОГДА не «объявлена». RoleLifecycleUnknown RoleLifecycleState = iota // RoleLifecycleDeclared — строка жива: объявление за ней стоит. RoleLifecycleDeclared // RoleLifecycleWithdrawn — строка помечена снятой: объявления больше нет. RoleLifecycleWithdrawn )
func (RoleLifecycleState) String ¶
func (s RoleLifecycleState) String() string
String — состояние словом. Для журналов и текстов отказа, не для контракта.
type RoleName ¶
type RoleName string
func (RoleName) Validate ¶
Validate — форма имени роли БЕЗ различения яруса: годна любая из двух. Оставлена для вызывающих, которым ярус неизвестен; сама сущность судится `ValidateAtTier`, потому что ограничений в таблице ДВА и каждое условлено вычисляемым `is_system`.
func (RoleName) ValidateAtTier ¶
ValidateAtTier — форма имени роли ПО ЯРУСУ, зеркало двух ограничений таблицы.
Различать обязательно: в базе стоят ДВА условленных ограничения (`roles_custom_name_check` под `is_system OR …` и `roles_system_name_check` под `NOT is_system OR …`), и правило, не различающее ярусов, расходится с ними в обе стороны сразу — принимает точечное имя у пользовательской роли и отвергает его у системной. Отказ тогда приезжает от базы (SQLSTATE 23514) без имени поля и без координаты.
type RoleRetirement ¶
type RoleRetirement struct {
// Marked — строка роли помечена снятой. Ложно означает, что оператор пометки
// не нашёл своей строки: роль уже снята либо владелец не тот.
Marked bool
// ResettledVerbs — строк проекции глаголов переселено в ведомость и снято.
ResettledVerbs int
// ResettledRuleRefs — строк проекции сегментов переселено и снято.
ResettledRuleRefs int
// RemovedSelectors — строк проекции отбора снято. В ведомость они не
// переселяются: у строки селектора нет пары «тип + глагол», которой
// адресуются сироты.
RemovedSelectors int
// RemovedTargetMembers — строк материализованного состава цели снято.
RemovedTargetMembers int
}
RoleRetirement — перепись ОДНОГО отзыва роли: что унесено и что помечено.
Числа возвращаются, а не логируются внутри: вызывающий собирает из них перепись применения, а «снято» без чисел неотличимо от «прошло мимо».
type RoleRuleRef ¶
type RoleRuleRef struct {
Module string
Resource string
// Verb — пустая строка означает ЯКОРЬ (глаголы не сужены), а не отсутствие
// значения: см. абзац выше.
Verb string
}
RoleRuleRef — один ОБЪЯВЛЕННЫЙ сегмент правила роли: «эта роль называет вот этот (модуль, ресурс) и вот этот глагол».
Чем это НЕ является — и различие несущее ¶
Это НЕ `RoleVerb`. Та проекция отвечает на вопрос вердикта «разрешено ли действие» и содержит только то, что РЕЗОЛВИТСЯ: тип, которого не знает словарь модели, пар не даёт (`RoleVerbsFromSelectors`). Здесь наоборот — строка кладётся на КАЖДЫЙ объявленный сегмент, резолвится он или нет, потому что предмет этой таблицы есть ссылочная целостность: молчаливый пропуск и есть тот дефект, ради которого заводится ключ, и воспроизвести его в новом писателе значило бы завести ключ, которому нечего отвергать.
Пустой глагол — это ЯКОРЬ, а не «глагол не задан» ¶
Правило, не сузившее глаголы (`verbs: ["*"]`), даёт строку с пустым `Verb`, которая ложится в хранилище значением NULL. Ключ ресурса на ней проверяется, ключ глагола — пропускается `MATCH SIMPLE`, и это ПРАВИЛЬНО: ресурс уже проверен первым ключом. Ключей поэтому два, а не один: под одним составным ключом `MATCH SIMPLE` снял бы проверку целиком, и правило, называющее несуществующий ресурс, принималось бы успешно.
func RuleRefsOf ¶
func RuleRefsOf(rules Rules) []RoleRuleRef
RuleRefsOf — объявленные сегменты правил в форме строк проекции.
Источник — АВТОРСКОЕ правило, а не селекторы: селекторы уже прошли через словарь модели и потеряли то, что он не знает, — то есть ровно те сегменты, ради которых ключ и заводится. Подстановка `*` в ресурсе и модуле сегментов не даёт: она называет не имя, а «все», и адресовать ею строку каталога нечего (системная роль с `*.*` материализуется коротким замыканием администратора кластера).
func (RoleRuleRef) Dotted ¶
func (r RoleRuleRef) Dotted() string
Dotted — точечная форма имени типа (`vpc.network`), та самая, какой говорят `role_verb.object_type`, `role_rule_selectors.object_types` и колонка `catalog_resource.dotted` под своим CHECK.
ЕДИНСТВЕННОЕ место, где эта склейка пишется в Go: второе написание разошлось бы с первым молча, а соединение по разным словарям не совпадает никогда — и отличить это от «права нет» было бы нечем.
func (RoleRuleRef) IsAnchor ¶
func (r RoleRuleRef) IsAnchor() bool
IsAnchor — правило не сузило глаголы.
type RoleScopeGroup ¶
type RoleScopeGroup int8
RoleScopeGroup — server-computed scope tier of a role, surfaced to the UI in AssignableRole.scope_group so the picker groups without client-side logic Maps 1:1 to the proto ScopeGroup enum.
const ( RoleScopeGroupUnspecified RoleScopeGroup = 0 RoleScopeGroupSystem RoleScopeGroup = 1 RoleScopeGroupAccount RoleScopeGroup = 2 RoleScopeGroupProject RoleScopeGroup = 3 )
func ScopeGroupOf ¶
func ScopeGroupOf(r Role) RoleScopeGroup
ScopeGroupOf derives the scope tier from the role's scope columns. System roles are SYSTEM; otherwise the non-empty custom scope decides (account → ACCOUNT, project → PROJECT).
func (RoleScopeGroup) String ¶
func (g RoleScopeGroup) String() string
String — debug rendering (matches the proto enum names).
type RoleSegment ¶
type RoleSegment struct {
RoleID RoleID
ObjectType string
// Verb — пустая строка означает ЯКОРЬ (правило не сузило глаголы): годится
// любая строка проекции своего типа.
Verb string
}
RoleSegment — объявленный сегмент роли в форме, которой спрашивают проекцию.
Точечный тип приходит ОДНИМ выражением (RoleRuleRef.Dotted) — тем же, каким его строит переселение и каким его хранит каталог. Второе написание разошлось бы молча: соединение по разным словарям не совпадает никогда.
func SegmentsOf ¶
func SegmentsOf(id RoleID, rules Rules) []RoleSegment
SegmentsOf — объявленные сегменты роли в форме вопроса к проекции.
type RoleVerb ¶
RoleVerb — одна пара проекции «роль даёт этот глагол на этом типе».
Тип — тип объекта модели прав (`vpc_network`), не точечное имя ресурса: вердикт спрашивает именно им. Глагол — в канонической форме и БЕЗ приставки отношения: приставку знает компилятор модели, и дублировать её здесь значило бы завести второе место, где она может смениться.
type RotatedRefreshToken ¶ added in v0.5.0
type RotatedRefreshToken struct {
Context CeremonyContext
Generation int32
}
RotatedRefreshToken — то, что вернул ОДИН оператор ротации.
type Rule ¶
type Rule struct {
Module string // exactly one module per rule
Resources []string
Verbs []string
ResourceNames []string
MatchLabels map[string]string
// Children — вложенные правила (kaname#874, nested_rule.go): правила над
// типами, лежащими под типом-контейнером этого правила. nil — вложенных нет;
// пустой непустой срез формой не является (второй способ сказать «нет»).
// У вложенного правила Module пуст (берётся у владельца) и своих Children нет.
Children []Rule
}
Rule — one authored grant over ONE module. The element lists are validated 1..16 each, the selector is resource_names XOR match_labels.
func (Rule) EffectiveChildren ¶ added in v0.5.0
EffectiveChildren — вложенные правила с модулем правила-владельца, как их читают проекции (селекторы, сегменты, «только чтение»). Хранимая и контрактная форма модуля у вложенного правила не несёт.
func (Rule) Fingerprint ¶
Fingerprint returns the content-hash of a rule (rule_fp). It is order-stable: the element lists and matchLabels keys are sorted before hashing, so the SAME rule authored in a different field order produces the SAME fp. The arm marker is folded in so an ARM_NAMES and an ARM_LABELS rule over the same modules/resources/verbs never collide.
The digest covers EVERY semantic field of the rule (module, resources, verbs, resource_names, match_labels) so a change to any of them — including a single label value or one verb — yields a different fp (the reorder/remove invariant rests on this).
func (Rule) OwnerType ¶ added in v0.5.0
OwnerType — точечный тип правила-владельца (`<модуль>.<ресурс>`); пусто у правила, которое владельцем быть не может (не ровно один ресурс либо `*`).
func (Rule) ReadsWholeType ¶ added in v0.5.0
ReadsWholeType — правило называет тип `dotted` и открывает его чтение целиком ([readsWhole]). Им судят ворота выдачи классу (SC-65).
func (Rule) Validate ¶
func (r Rule) Validate(policy RulePolicy, modules ModuleSet) error
Validate enforces the rule form. The wildcard relaxation is carried by RulePolicy — a single value derived from the row by PolicyOfRole — rather than by a boolean "system context": the module tier needs a THIRD policy, not a second boolean (see rule_policy.go). Errors carry the stable texts so the API contract is preserved.
`modules` — НАБОР МОДУЛЕЙ, каким его знает вызывающий (см. module_set.go). Он приходит параметром, потому что домен закрытого набора не объявляет: на пути запроса это ЖИВЫЕ строки каталога, у оснастки дерева — канон. Отсутствие набора отвергается самой строгой ветвью: «не знаю» не есть «можно».
type RuleLabelSelector ¶
type RuleLabelSelector struct {
RuleFP string
ObjectTypes []string
MatchLabels map[string]string
Verbs []string
}
RuleLabelSelector — the reconciler-facing projection of ONE ARM_LABELS rule: the rule_fp it is keyed by, the dotted object types it selects (cartesian modules×resources), the matchLabels equality selector, and the authored verbs (which drive the per-object tier + v_* relations the reconciler emits — the tier is NOT taken from the role's compiled permissions, since ARM_LABELS rules are excluded from CompileRules). It carries no pgx/grpc — pure domain.
type RuleLifecycle ¶
type RuleLifecycle uint8
RuleLifecycle — состояние ОДНОГО правила роли.
Нулевой вариант несёт ЕДИНСТВЕННЫЙ смысл: «этим ответом не вычислено». RuleStatesOf его не возвращает никогда — иначе «посчитано, терять нечего» стало бы неотличимо от «не считали».
const ( // RuleLifecycleUnknown — не вычислено ЭТИМ ответом. НИКОГДА не «действует». RuleLifecycleUnknown RuleLifecycle = iota // RuleLifecycleActive — ни один адресуемый сегмент правила не потерян. RuleLifecycleActive // RuleLifecycleWithdrawn — потерянные сегменты есть, и КАЖДЫЙ объяснён // ведомостью переселения: объявление сняла платформа. RuleLifecycleWithdrawn // RuleLifecycleUnresolved — потерянные сегменты есть, и ХОТЯ БЫ ОДИН не // объяснён: правило ссылается на то, чего сейчас нет. RuleLifecycleUnresolved )
type RuleNameHold ¶ added in v0.7.0
type RuleNameHold struct {
Rule int
Ref ResourceRef
}
RuleNameHold — ключ объекта, названный правилом `Rule` (индекс в `Rules`).
type RulePolicy ¶
type RulePolicy struct {
// contains filtered or unexported fields
}
RulePolicy — политика послабления подстановки в правилах роли.
Почему политик ТРИ, а не две (задача продукта kacho#1032) ¶
До неё политика была одна булева — «системный контекст». Признак `is_system` вычисляется ровно из непустого кластерного якоря и несёт ДВА смысла сразу: «арендатор эту роль не правит» и «этой роли можно подставлять звёздочку». Базовая роль модуля обязана быть системной в первом смысле — иначе её правит арендатор, — и вместе с ним получала второй: прямой путь к `*.*.*`. В диффе это выглядит обычной строкой роли, и ни один обзор её не поймает.
Политики различает ВЛАДЕЛЕЦ, а не ярус:
арендаторская якоря кластера нет module: "*" ✗ ресурс "*" ✗ модульная якорь есть, владелец назван module: "*" ✗ ресурс "*" ✓ только в своём модуле платформенная якорь есть, владельца нет module: "*" ✓ ресурс "*" ✓
Одно правило, а не три исключения: подстановка в роли с владельцем законна ровно в пределах её модуля. Отсюда обе строки механически — `module: "*"` не находится ни в одном модуле, поэтому отвергается всегда; ресурс `*` находится в модуле своего правила, поэтому законен ровно тогда, когда этот модуль и есть владелец.
Глагол `*` не затрагивается — решение, а не пропуск ¶
Он разрешён и в арендаторской роли, безусловно ([validateVerbs]): он не сегмент пространства имён, а «все действия названного типа». Сузить его здесь значило бы отобрать уже выданное под видом починки.
Носитель — ЗНАЧЕНИЕ, а не второй булев флаг ¶
Два булевых параметра дают четыре сочетания при трёх законных, и четвёртое («не системная, но с владельцем») пришлось бы отвергать четвёртым правилом. Политика передаётся одним значением закрытого перечня, выводимым из строки функцией ОДНОГО места — PolicyOfRole. Второе объявление «системная ли роль для целей подстановки» есть находка, и её ловит гейт дерева.
func PolicyOfRole ¶
func PolicyOfRole(isSystem bool, ownerModule string) RulePolicy
PolicyOfRole выводит политику ИЗ СТРОКИ — единственное место в дереве, где это делается.
`isSystem` берётся оттуда же, откуда его берёт база: из непустого кластерного якоря. `ownerModule` пуст у платформенной роли и непуст у роли, объявленной манифестом модуля.
Сочетание «не системная, но с владельцем» законным не является: владельца пишет только применитель ролей модуля, а он объявляет роль системной безусловно. Такой вход политику получает АРЕНДАТОРСКУЮ — самую строгую: послабление выдаётся по доказанному признаку, а не по частично совпавшему.
func TenantPolicy ¶
func TenantPolicy() RulePolicy
TenantPolicy — арендаторская политика в чистом виде.
Нужна вызывающему, у которого строки роли нет вовсе: загрузчик манифеста судит правила ДО того, как роль станет строкой, и обязан судить их самой строгой политикой. Порядок — часть контракта (сценарий IAM-OM-1-18): отказ приходит от загрузчика с координатой строки YAML, а не от применителя, у которого координаты нет.
func (RulePolicy) OwnerModule ¶
func (p RulePolicy) OwnerModule() string
OwnerModule — модуль-владелец роли; пусто у арендаторской и платформенной.
func (RulePolicy) UpdateImpliesDelete ¶ added in v0.7.0
func (p RulePolicy) UpdateImpliesDelete() bool
UpdateImpliesDelete — влечёт ли правка удаление на листовом типе (`authzmap.GrantedVerbsWithDeclared`, правило «правка влечёт удаление»).
Почему у модульной роли — нет (kaname#1006) ¶
Правило заведено для ролей, чей набор глаголов пишет человек в плоской модели: редактор, получивший правку, без удаления не убрал бы за собой свой же объект. Роль модуля устроена иначе — её классы объявляет манифест модуля, и объявляет ТОЧНО: модуль, желающий дать удаление, называет его классом (так устроена роль-администратор модуля), а роль, назвавшая только правку, удалять не вправе. Добавленное здесь удаление было бы правом, которого модуль не объявлял.
Нулевая политика ответа «да» не получает: собранная не через PolicyOfRole политика судится самой строгой ветвью — меньше прав, а не больше.
type RuleSelector ¶
type RuleSelector struct {
RuleFP string
Arm Arm
ObjectTypes []string
ResourceNames []string // ARM_NAMES only
MatchLabels map[string]string // ARM_LABELS only
Verbs []string
// Container — контейнер вложенного правила (kaname#874); nil у правила
// верхнего уровня. Селектор с контейнером охватывает объекты своих
// ObjectTypes, у которых среди предков есть объект типа Container.Type,
// прошедший отбор Container и входящий в цель выдачи.
Container *ContainerSelector
// Grants — глаголы, которые ЭТО правило даёт на каждом своём типе (ключ —
// точечный тип из ObjectTypes), по живому каталогу (kaname#878). Решение о
// доступе даёт глагол на объекте только тем правилом, чей селектор объект
// выбрал: глагол соседнего правила той же роли охвата этого правила не
// получает.
//
// nil — НЕ ВЫЧИСЛЕНО (заполняет `catalog.Facts.GrantSelectors`), и писатель
// селекторов такую строку отвергает: молча записанное «правило не даёт
// ничего» было бы отказом, неотличимым от честного. Пустая карта — вычислено:
// живой каталог не даёт правилу ни одного глагола.
//
// Карта — функция ОТПЕЧАТКА правила (RuleFP — хэш всего содержимого правила,
// включая глаголы и вложенные), каталога и владельца роли (политика роли
// решает, влечёт ли правка удаление, kaname#1006): от прочих правил роли она
// не зависит.
Grants map[string][]string
}
RuleSelector — the UNIFIED reconciler-facing projection of ONE materializing rule (RBAC explicit-model 2026). It carries the arm so the reconciler picks the match strategy: ARM_ANCHOR(all) → every object of ObjectTypes inside scope; ARM_NAMES → only ResourceNames; ARM_LABELS → labels @> MatchLabels. The per-object FGA tuples (v_* + tier) are derived from Verbs (the tier is NOT taken from the role's compiled permissions — ARM_LABELS/ARM_ANCHOR-materialized rules are not in CompileRules). Pure domain, no pgx/grpc.
func (RuleSelector) PublicReadsChildOf ¶ added in v0.5.0
func (s RuleSelector) PublicReadsChildOf(containerType string, children []string) bool
PublicReadsChildOf — селектор вложенного правила под контейнером `containerType`, читающий ЦЕЛИКОМ одного из его прямых детей `children`. Им судит разворачивание выдачи классу, писать ли `public_reader` (ND-03). Правило верхнего уровня (прежняя форма) `public_reader` не пишет (ND-07 (б)).
func (RuleSelector) ReadsWholeType ¶ added in v0.5.0
func (s RuleSelector) ReadsWholeType(dotted string) bool
ReadsWholeType — то же правило в форме селектора материализации ([readsWhole]). Им судит разворачивание выдачи, когда решает, писать ли `public_reader`.
type RuleState ¶
type RuleState struct {
// RuleIndex — индекс правила в `Role.Rules`. КЛЮЧ записи: порядок самих
// записей не значим, сверять надо по нему.
//
// Индекс законен как ключ потому, что `roles.rules` — массив JSONB под одним
// кодеком, и порядок сохраняется by construction: правило читается тем же
// индексом, каким записано. Действителен он в пределах ОДНОГО ответа:
// `Update` перестраивает массив, и мутацию индекс не переживает.
RuleIndex int
// State — состояние правила. У вычисленного оно непусто ВСЕГДА.
State RuleLifecycle
// Segments — сколько АДРЕСУЕМЫХ сегментов объявляет это правило.
// Ноль законен (подстановка в модуле или ресурсе сегментов не даёт).
//
// ЕДИНИЦА ДРУГАЯ, ЧЕМ У [RoleIntegrity.Declared], и потому имя другое:
// счётчик роли дедуплицирует сегмент по ВСЕЙ роли, здесь — только внутри
// правила. Равенство сумм поэтому НЕ ГАРАНТИРОВАНО и нарушается ровно
// тогда, когда один сегмент назван двумя правилами; на прочих ролях суммы
// совпадают, и это совпадение, а не инвариант.
Segments int
// Lost — сколько из объявленных сегментов этого правила не дают ни одной
// строки проекции, которую читает вердикт.
//
// ПРЕДИКАТ ТОТ ЖЕ, что у [RoleIntegrity.Unresolved]: ведомость здесь не
// спрашивается — её спрашивает [RuleState.Explained] рядом. Различает
// величины только единица дедупликации.
Lost int
// Explained — сколько из потерянных ОБЪЯСНЕНЫ ведомостью переселения.
// Необъяснённые выводятся вычитанием: третье поле, равное разности двух
// соседних, разошлось бы с ними при первой же правке.
Explained int
// Children — состояния вложенных правил ЭТОГО правила (kaname#874), в
// порядке `Rule.Children`; RuleIndex у записи — индекс в Children. Пусто у
// правила без вложенных.
Children []RuleState
}
RuleState — состояние одного правила роли и три величины, из которых оно выведено.
Почему слово И счётчики, а не одно из двух ¶
Правило, у которого часть потерь объяснена, а часть нет, читается RuleLifecycleUnresolved — слово называет состояние, ТРЕБУЮЩЕЕ разбора. Схлопыванием это не является ровно потому, что рядом стоят обе величины: `Withdrawn` и `Unresolved` видны одновременно, и ни одна не потеряна.
Четвёртое значение («частично отозвано») рассмотрено и отвергнуто: у него нет ни своего действия у арендатора, ни своего производителя, при том что счётчики уже несут ровно ту же величину.
func RuleStatesOf ¶
func RuleStatesOf(rules Rules, unresolved []RoleSegment, withdrawn []WithdrawnGrant) []RuleState
RuleStatesOf — состояние каждого правила роли. ЕДИНСТВЕННОЕ место, где решается, какое состояние несёт правило.
Вход:
- rules — авторские правила роли;
- unresolved — сегменты роли, не давшие НИ ОДНОЙ строки проекции, которую читает вердикт. Источник состояния;
- withdrawn — ведомость переселения. Только ОБЪЯСНЯЕТ: судить состояние по ней значило бы читать роль, пострадавшую вторым путём, действующей.
Записей в результате ровно `len(rules)`, и `RuleIndex` каждой указывает на своё правило.
type Rules ¶
type Rules []Rule
Rules — a role's authored policy. Cardinality 1..64.
func DecodeRules ¶
DecodeRules decodes the roles.rules JSONB payload into domain Rules. An empty/nil payload yields nil (a legacy permissions-only role, rules='[]').
func OwnerRoleRules ¶
func OwnerRoleRules() Rules
OwnerRoleRules is the canonical authored policy of the `owner` system-role: `[{module:"*", resources:["*"], verbs:["*"]}]` (the `*.*.*` "selector all" shape). It MUST stay byte-for-byte semantically in lockstep with the migration 0035 seed (`rules` JSONB column). Exposed so the seed layer can derive the owner role's materializing selectors (role_rule_selectors) for the forward fast-path WITHOUT re-encoding the wildcard-expansion type set in SQL.
func (Rules) CoversType ¶
CoversType сообщает, дают ли авторские правила роли глаголы на точечный тип `<module>.<resource>` (redesign-2026 F9, ворота 3 / IAM-1-24). Правило покрывает тип, когда совпадает Module, а Resources перечисляет ресурс (или "*"). Пустые правила не покрывают ничего.
func (Rules) HasAnchorRule ¶
HasAnchorRule reports whether any rule in the set uses the ARM_ANCHOR selector (selector=all: neither resource_names nor match_labels). An ARM_ANCHOR rule materializes over ALL instances under scope — on a GLOBAL (cluster) scope that is the cluster-wide per-object set Q-2/A-05 forbids for ordinary roles.
func (Rules) HasChildren ¶ added in v0.5.0
HasChildren — у роли есть хотя бы одно вложенное правило.
func (Rules) LabelSelectors ¶
func (rs Rules) LabelSelectors() []RuleLabelSelector
LabelSelectors projects a role's rules to the ARM_LABELS selectors the reconciler materializes. ARM_ANCHOR / ARM_NAMES rules are excluded (they emit at Create-time, not via the reconciler). The dotted types are `{module}.<resource>` over each label rule's resources (the module is scalar — no module unroll; wildcards are already rejected on a label rule by Rule.Validate, so no `*` reaches here in a well-formed role).
Retained as the ARM_LABELS-only view (back-compat). RBAC explicit-model 2026 routes the reconciler through MaterializingSelectors (ALL arms); use that for the unified materializer.
func (Rules) MaterializingSelectors ¶
func (rs Rules) MaterializingSelectors() []RuleSelector
MaterializingSelectors projects a role's rules to the UNIFIED selector set the reconciler materializes — ARM_ANCHOR(all) + ARM_NAMES + ARM_LABELS. Binding-time scope_grant emission is removed; the reconciler is the single path.
This is the SCOPE-AGNOSTIC (role-level) projection used to persist role_rule_selectors (the forward fast-path JOIN index): a wildcard `*.*` rule is EXPANDED to the full materializable type set so a freshly-registered object fast-path-matches an owner binding. The per-binding scope gate (MaterializingSelectorsInScope, consumed by the reconciler's LoadBinding) still prevents a GLOBAL/CLUSTER binding from per-object materializing, so expanding the role-level index is safe — the index over-includes, the binding scope narrows.
func (Rules) MaterializingSelectorsInScope ¶
func (rs Rules) MaterializingSelectorsInScope(scope Scope) []RuleSelector
MaterializingSelectorsInScope is the SCOPE-AWARE projection the reconciler uses to compute a binding's desired membership (LoadBinding). A wildcard `*.*` rule is expanded to the full materializable type set ONLY for a BOUNDED scope (ACCOUNT/PROJECT) — per-object owner content. For a GLOBAL/CLUSTER scope the wildcard yields NO ObjectTypes: cluster super-admin is the flat short-circuit, never per-object. A non-wildcard rule is scope-independent (its dotted types are explicit).
func (Rules) NamedKeys ¶ added in v0.7.0
func (rs Rules) NamedKeys() []RuleNameHold
NamedKeys — каждый ключ объекта, который правила роли называют в `resourceNames`, вместе с индексом правила: тип — точечное имя `<module>.<resource>`, вложенное правило берёт модуль владельца. Подстановка конкретного типа не называет.
func (Rules) ScopeSelfVerbs ¶
ScopeSelfVerbs returns the UNION of authored verbs the role's rules grant on the binding's OWN scope resource-type — i.e. on the scope object itself (`account:<X>`/`project:<X>`/`cluster:<X>`). RBAC explicit-model 2026 P4 (D-7 / КФ-3 / C-01): a rules-role bound on a scope must materialize its tier (+ verb- bearing v_*) ON THE SCOPE ANCHOR ITSELF — the write-authz anchor / self-access that the removed binding-time scope_grant/anchor emit produced. The reconciler is now the SINGLE materialization path, so this projection feeds a scope-self desired member (reconcile.desiredRuleMembers), NOT a binding-time emit.
A rule contributes its verbs when EITHER its (module,resource) is the FULL `*.*` wildcard (the system superuser shape — migration 0031: admin/edit/view) OR its (module,resource) is exactly ("iam", scopeResource) — e.g. an `iam.account` rule on an account-scoped binding. scopeResource is the scope's resource type: "account"|"project". (cluster has no per-resource iam rule; only the `*.*` superuser shape grants cluster-self — handled by the wildcard branch.)
Returns nil when no rule applies to the scope self (a content-only role — e.g. `compute.instance` rules — grants nothing ON the account/project object, only on its content; the scope-self member is then absent, fail-closed).
func (Rules) Validate ¶
func (rs Rules) Validate(policy RulePolicy, modules ModuleSet) error
Validate validates the rule set: cardinality 1..64; each rule self-valid.
The policy is a single value derived from the row by PolicyOfRole; see rule_policy.go for why there are three of them and not two. `modules` — набор модулей вызывающего, см. module_set.go.
func (Rules) WithoutResourceName ¶ added in v0.7.0
WithoutResourceName возвращает правила роли без ключа объекта (`dottedType`, `id`): id перестаёт называться на типе `dottedType` в каждом правиле и каждом вложенном правиле, которое называет его на этом типе.
КЛЮЧ — ПАРА (тип, id), А ПЕРЕЧЕНЬ ИМЁН ОДИН НА ВСЕ ТИПЫ ПРАВИЛА. Правило над `resources: [a, b]` с `resourceNames: [N]` даёт и (a, N), и (b, N); снятие (a, N) обязано отнять только первое — (b, N) другой объект (Д1, URG-05). Поэтому правило, называющее снятый тип вместе с другими, расщепляется: прежнее правило остаётся над остальными типами с прежними именами, а снятый тип уходит в своё правило с именами без id. Правило над одним снятым типом просто теряет имя.
ПУСТОЙ ПЕРЕЧЕНЬ ИМЁН НЕ ХРАНИТСЯ. Правило без имён — ветвь `anchor`, то есть все объекты типа в области; хранить его значило бы расширить доступ ровно там, где снятие обязано его сузить. Поэтому правило верхнего уровня, у которого имён не осталось, снимается (`removed` считает правила, исчезнувшие целиком), а вложенное — снимается из `Children` (правило-владелец остаётся).
ПРЕДЕЛ ЧИСЛА ПРАВИЛ. Расщепление добавляет правило. Если правил роли (или вложенных правил одного владельца) после него стало бы больше предела (`MaxRules`, `maxChildren`), снятый id вычёркивается из общего перечня этого правила целиком — сужение вместо расширения: доступ к (b, N) теряется, но снятый ключ не остаётся названным, и роль не выходит за предел, который её чтение отвергло бы.
Тип правила — точечное имя `<module>.<resource>`; у вложенного правила модуль берётся у владельца. Правило над подстановкой (`*`) имён не несёт (`Validate`) и не трогается. Правило с меткой (`MatchLabels`) имён не несёт.
`changed = false` — ни одно правило ключ не называло; вызывающий тогда роль не пишет вовсе (сужения нет, строки аудита нет — URG-22, URG-37).
type SAOAuthClientID ¶
type SAOAuthClientID string
SAOAuthClientID — новый формат `soc<17-crockford>` (corelib `ids.NewID`, без подчёркивания). id существующих строк immutable (id = имя клиента + JWK kid), поэтому валидатор принимает и прежнюю форму `soc_<17-crockford>`.
func (SAOAuthClientID) Validate ¶
func (id SAOAuthClientID) Validate() error
type Scope ¶
type Scope int8
Scope — anchor tier for an AccessBinding.
func DeriveFromResourceType ¶
DeriveFromResourceType — best-effort fallback for code paths that have resource_type but no explicit Scope (e.g. legacy callers that pre-date the W4 scope plumbing). Mirrors the BEFORE INSERT trigger of the schema; that the two agree is held by a probe, not by this sentence — см. §«Третье место» у [scopeVocabulary].
Вид вне словаря даёт ярус проекта — самый узкий из трёх, то есть ошибка умолчания не расширяет доступ.
func (Scope) ValidateAgainst ¶
ValidateAgainst checks that the Scope is consistent with the binding's (resource_type, resource_id). Returns ErrScopeMismatch if not.
CLUSTER ⇒ resource_type='cluster', resource_id='cluster_root' ACCOUNT ⇒ resource_type='account', resource_id starts with 'acc' PROJECT ⇒ resource_type='project', resource_id starts with 'prj'
Виды и их требования к идентификатору берутся из [scopeVocabulary]; своего перечисления здесь нет — оно было и разошлось бы с картой молча.
type ScopeAnchor ¶
ScopeAnchor — the resource the binding is scoped to (its containment anchor). Type is one of "project" | "account" | "cluster"; ID is the resource id.
type SeedIdentityRename ¶ added in v0.3.0
type SeedIdentityRename struct {
// Previous — написание применённого свода. Его продолжают присылать
// непереведённые манифесты.
Previous string
// Declared — написание, объявленное §2.3 приёмки.
Declared string
// Subject — ЧИТАТЕЛЬ, ради которого запись существует. Без него запись
// неотличима от послабления «на всякий случай».
Subject string
}
SeedIdentityRename — одно переименование посевной идентичности: как её звали в применённом своде и как зовут по объявленному решению.
func SeedIdentityWindow ¶ added in v0.3.0
func SeedIdentityWindow() []SeedIdentityRename
SeedIdentityWindow возвращает окно целиком. Копия — чтобы вызывающий не мог расширить приём, правя чужой срез.
type SelectorPruneOutcome ¶
type SelectorPruneOutcome uint8
SelectorPruneOutcome — что стало со строкой ОТБОРА, из которой вырезан тип.
Величины разделены намеренно: строка, потерявшая последний живой тип, снята целиком — правило перестало отбирать вовсе; строка, у которой живой тип остался, укорочена — правило сузилось. Для разбирающего последствия это события разного рода, и сложив их, мы потеряли бы ровно это различие.
const ( // SelectorPruneOutcomeUnknown — исход не прочитан ЭТИМ ответом. Никогда не // означает «исход неизвестен»: ведомость несёт его всегда, закрытым набором // на уровне схемы (`role_selector_prune_outcome_known`). SelectorPruneOutcomeUnknown SelectorPruneOutcome = iota // SelectorPruneOutcomeShortened — живой тип у строки остался, она укорочена: // правило продолжает отбирать, но уже меньше. SelectorPruneOutcomeShortened // SelectorPruneOutcomeDropped — живого типа не осталось, строка снята // целиком: правило не отбирает ничего. SelectorPruneOutcomeDropped )
type ServiceAccount ¶
type ServiceAccount struct {
ID ServiceAccountID
AccountID AccountID
Name SvcAccountName
Description Description
Enabled bool // default true
CreatedAt time.Time
// Labels — tenant-facing метки. Делают ServiceAccount label-selectable
// наравне с account/project (ARM_LABELS-грант на iam.serviceAccount → v_list
// по `labels @> matchLabels`; List фильтрует viewer ∪ v_list).
Labels Labels
}
ServiceAccount — Account-scoped (account_id FK ON DELETE RESTRICT).
Проектной области у служебной учётки нет: поле `project_id` и его колонка сняты — заполнить их было нечем (ни запроса, ни записи, ни выборки), а claim, который из них выводился, не читал никто. Понадобятся проектные служебные учётки — они заводятся отдельной подсистемой со своей приёмкой.
func (ServiceAccount) MayAuthenticate ¶
func (s ServiceAccount) MayAuthenticate() bool
MayAuthenticate reports whether this service account is allowed to obtain a token or a fresh credential. It is the single predicate every issuance path asks, the machine counterpart of InviteStatus.MayAuthenticate for users — so that no path can re-derive its own answer and quietly disagree with the rest.
It reads a field, which means it is only as truthful as the query that populated the struct: `enabled` is a bool, false in every zero value, so a read that does not select the column makes this method answer "no" for every account in existence. Callers therefore judge a row they actually read, and the reads that feed them are pinned by their own tests.
func (ServiceAccount) Validate ¶
func (s ServiceAccount) Validate() error
type ServiceAccountID ¶
type ServiceAccountID string
type ServiceAccountOAuthClient ¶
type ServiceAccountOAuthClient struct {
ID SAOAuthClientID
SvaID ServiceAccountID
Description Description
CreatedByUserID UserID
CreatedAt time.Time
ExpiresAt *time.Time
LastUsedAt *time.Time
// PublicKeyPEM — SPKI-encoded ECDSA P-256 public key of the key pair; the
// signature of `client_assertion` is checked against it. Empty for FEDERATED
// and SECRET rows: the first presents the external issuer's key, the second
// carries no key material at all.
PublicKeyPEM string
// KeyAlgorithm — JOSE alg of the key pair. One of {"ES256", "RS256",
// "EdDSA"}; new private_key_jwt keys always set "ES256"; federated and
// secret rows leave it empty.
KeyAlgorithm string
// TrustedSubjects — федеративный вид ключа. Непустой перечень означает, что
// удостоверение предъявляет ВНЕШНИЙ издатель по RFC 7521/7523: своего
// ключевого материала строка не несёт вовсе, а подпись проверяется ключом
// издателя из НАШЕГО перечня доверенных издателей (задача kacho#1124), который
// читает проверяющий утверждения на пути запроса.
//
// Каждый элемент сужает, какая внешняя пара `(iss, sub)` вправе выступать
// за этот ключ. Пустой перечень — обычный вид с ключевым материалом.
TrustedSubjects []TrustedSubject
// DeclaredAudiences — сужение адресатов, ОБЪЯВЛЕННОЕ заказчиком при выдаче
// (`IssueSAKeyRequest.audience`, задача kacho#1136). Create-only, как и всё
// остальное на этом ресурсе: глагола правки у ключа нет.
//
// Перечней в тракте выдачи ДВА, и этот — внутренний: он говорит, для чего
// заведён ЭТОТ ключ, и действует ВНУТРИ перечня, объявленного посадкой.
// Расширить внешнюю границу он не может ничем.
//
// Пустой перечень означает «сужения не объявлено», а не «любой адресат»:
// внешняя граница остаётся и требуется непустой стражем старта выдачи.
DeclaredAudiences []string
// CredentialKind — вид удостоверения. ЗАПИСЫВАЕТСЯ при вставке; читателем
// не вычисляется и из состава прочих полей не выводится.
CredentialKind CredentialKind
// SecretHash — sha256 по идентификатору строки И секретной части вместе,
// 32 байта. Непуст ТОЛЬКО у вида SECRET. Сам секрет не хранится нигде: он
// существует только в теле ответа, полученного вызывающим выдачи.
SecretHash []byte
// Name — человекочитаемое имя ключа, выставляется на Issue (create-only,
// immutable — ресурс несёт только Issue/List/Revoke). Пусто для legacy-строк.
Name OAuthClientName
// Labels — произвольные метки ключа, выставляются на Issue (create-only,
// immutable). Пусто для legacy-строк.
Labels Labels
}
ServiceAccountOAuthClient — ключ служебной учётки (Class A workload identity).
Клиентом ключ называется по идентификатору ЭТОЙ строки: им подписывается `client_assertion`, по нему наш реестр утверждений разрешает клиента, и им же ключ называют докерная полоса и отзыв. Второго имени у ключа нет (kaname#362): имя клиента у прежнего внешнего издателя снято вместе со столбцом, где оно лежало.
private_key_jwt: служба чеканит пару ключей ECDSA P-256 на каждый ключ, хранит открытую половину (SPKI PEM) и алгоритм и отдаёт закрытую половину вызывающему ровно один раз. Общего секрета у этого вида нет.
func (ServiceAccountOAuthClient) Validate ¶
func (c ServiceAccountOAuthClient) Validate() error
type SessionBearer ¶ added in v0.5.0
type SessionBearer struct {
// contains filtered or unexported fields
}
SessionBearer — непрозрачное значение носителя (Р1). Тип не печатается и не сериализуется, как `LoginVerifier`; значение выходит ровно двумя методами: `Digest` (в хранилище) и `CookieValue` (клиенту).
func NewSessionBearer ¶ added in v0.5.0
func NewSessionBearer() (SessionBearer, error)
NewSessionBearer — свежий носитель из криптографического источника случайности.
func PresentedSessionBearer ¶ added in v0.5.0
func PresentedSessionBearer(value string) SessionBearer
PresentedSessionBearer — носитель, как его прислал клиент. Значение не разбирается: единственное, что с ним делают, — берут дайджест. Пустое — отсутствие носителя.
func (SessionBearer) CookieValue ¶ added in v0.5.0
func (b SessionBearer) CookieValue() string
CookieValue — значение для `Set-Cookie`. Единственный выход к клиенту.
func (SessionBearer) Digest ¶ added in v0.5.0
func (b SessionBearer) Digest() BearerDigest
Digest — свёртка носителя, по которой хранилище находит запись. Хранится и сравнивается только она.
func (SessionBearer) Format ¶ added in v0.5.0
func (b SessionBearer) Format(f fmt.State, verb rune)
Format — заглушка на любом глаголе форматирования.
func (SessionBearer) GoString ¶ added in v0.5.0
func (b SessionBearer) GoString() string
GoString — заглушка и для `%#v`.
func (SessionBearer) IsZero ¶ added in v0.5.0
func (b SessionBearer) IsZero() bool
IsZero — носителя нет.
func (SessionBearer) LogValue ¶ added in v0.5.0
func (b SessionBearer) LogValue() slog.Value
LogValue — заглушка для журнала.
func (SessionBearer) MarshalJSON ¶ added in v0.5.0
func (b SessionBearer) MarshalJSON() ([]byte, error)
MarshalJSON — отказ.
func (SessionBearer) MarshalText ¶ added in v0.5.0
func (b SessionBearer) MarshalText() ([]byte, error)
MarshalText — отказ.
func (SessionBearer) String ¶ added in v0.5.0
func (b SessionBearer) String() string
String — заглушка.
type SessionRevocation ¶
type SessionRevocation struct {
TokenJTI string
RevokedAt time.Time
Reason string
UserID UserID
TTLExpiresAt time.Time
}
SessionRevocation — fast lookup table keyed by token_jti (PK). A cron cleanup deletes rows past ttl_expires_at (`DeleteExpired`, `pg/audit_session_revocation_repos.go`). Migration `0001_initial.sql`.
Номер миграции здесь был неверен — стояло 0013, а это про снятие перечня условий обхода. Правится вместе с тем же дефектом у соседа (`audit_outbox_entry.go`): радиус берётся по МЕХАНИЗМУ — «номер миграции, названный в шапке сущности», — а не по файлу, где дефект заметили.
func (SessionRevocation) Validate ¶
func (s SessionRevocation) Validate() error
type SigningAlgorithm ¶
type SigningAlgorithm string
SigningAlgorithm — алгоритм подписи токена. Словарь ЗАКРЫТ: значение вне него отвергается разбором конфигурации и стражем старта (приёмка F1 §3 строка 1, F1-03). Открытый словарь означал бы «принимаем любой» — тот же класс, что пустой перечень.
const ( SigningAlgRS256 SigningAlgorithm = tokenpolicy.AlgRS256 SigningAlgES256 SigningAlgorithm = tokenpolicy.AlgES256 SigningAlgEdDSA SigningAlgorithm = tokenpolicy.AlgEdDSA )
Закрытый словарь алгоритмов подписи. Значения ВЫВЕДЕНЫ из платформенного объявления (pkg/tokenpolicy), а не выписаны здесь второй раз: приёмная сторона живёт в другом сервисе, и две копии словаря разошлись бы молча — в сторону «принимаем больше», потому что расширять проще.
func ParseSigningAlgorithm ¶
func ParseSigningAlgorithm(raw string) (SigningAlgorithm, error)
ParseSigningAlgorithm разбирает значение конфигурации. Пустое значение — НЕ «умолчание»: у алгоритма подписи умолчания нет, потому что подпись умолчанием — решение, принятое за оператора.
func SigningAlgorithms ¶
func SigningAlgorithms() []SigningAlgorithm
SigningAlgorithms возвращает закрытый словарь целиком. Перечень ВЫВОДИТСЯ отсюда всеми, кому он нужен (страж старта, текст отказа, проверка ключа), а не выписывается по месту.
func (SigningAlgorithm) MinBits ¶
func (a SigningAlgorithm) MinBits() int
MinBits — нижний порог стойкости ключа, объявленный ЧИСЛОМ и ровно в одном месте дерева (F1-02). Для RSA это длина модуля; для кривых и Ed25519 длина задана самой кривой, поэтому порог у них выражен размером, который эта кривая даёт.
type SigningKeyRecord ¶
type SigningKeyRecord struct {
KID KeyID
Algorithm SigningAlgorithm
State SigningKeyState
PublicKeyPEM string
PrivateKeyWrapped []byte
CreatedAt time.Time
NotAfter time.Time
ActivatedAt *time.Time
RetiredAt *time.Time
RemovedAt *time.Time
CompromisedAt *time.Time
}
SigningKeyRecord — ХРАНИМАЯ форма ключа: несёт обёрнутую приватную половину.
Пара к PublishedKey. Разделение типов держит §6.10 КОМПИЛЯТОРОМ: форма «поле есть, но мы его не заполняем» держалась бы вниманием.
func (SigningKeyRecord) Published ¶
func (r SigningKeyRecord) Published() PublishedKey
Published — проекция строки в ПУБЛИКУЕМУЮ форму. Единственный переход между двумя типами, и он односторонний: обратного конструктора нет.
type SigningKeyState ¶
type SigningKeyState string
SigningKeyState — состояние ключа в ключнице.
Состояний ПЯТЬ, и «скомпрометирован» существует отдельно от «выведен» (приёмка §2.1): первое снимает ключ из набора немедленно, принимая отказ живых токенов, второе — нет. Глагол, делающий и то и другое, лишил бы второе решение его цены.
const ( // SigningKeyPublished — ключ в наборе, но ещё не подписывает. Этап // существует ровно ради порядка «в наборе → подписывает» (§6.1). SigningKeyPublished SigningKeyState = "PUBLISHED" // SigningKeyActive — ключ подписывает. Такой РОВНО ОДИН, и это держит // частичный уникальный индекс, а не проверка в коде (§6.2). SigningKeyActive SigningKeyState = "ACTIVE" // SigningKeyRetired — выведен из подписи, остаётся в наборе всю отсрочку: // подписанные им токены доживают свой срок (§6.4). SigningKeyRetired SigningKeyState = "RETIRED" // SigningKeyRemoved — отсрочка истекла, ключа в наборе нет. SigningKeyRemoved SigningKeyState = "REMOVED" // SigningKeyCompromised — покидает набор НЕМЕДЛЕННО; живые токены // отвергаются, и это принятая цена, а не дефект. SigningKeyCompromised SigningKeyState = "COMPROMISED" )
Состояния ключа.
func (SigningKeyState) CanActivate ¶
func (s SigningKeyState) CanActivate() bool
CanActivate отвечает, допускает ли машина состояний переход в ACTIVE.
Переход из REMOVED и COMPROMISED НЕ ВЫРАЖАЕТСЯ (F1-29): скомпрометированный ключ, вернувшийся в подпись, — не «редкий случай», а конструкция, которая получается сама, если её не запретить.
func (SigningKeyState) InKeySet ¶
func (s SigningKeyState) InKeySet() bool
InKeySet отвечает, попадает ли ключ в публикуемый набор.
Ответ следует СОСТОЯНИЮ, а не факту существования строки: набор, отдающий все строки подряд, отдал бы и снятый, и скомпрометированный.
type StepUpChallenge ¶ added in v0.5.0
type StepUpChallenge struct {
// Challenge — случайные байты; ключ строки. Длина — та же, что у прочих
// испытаний ключа (`AccessKeyChallengeBytes`).
Challenge []byte
// SessionID — запись сессии, которой испытание выдано. Одно ЖИВОЕ
// испытание на запись держит частичная уникальность схемы (ban #10).
SessionID HumanSessionID
// IssuedAt и ExpiresAt — момент выдачи и предел, часы службы.
IssuedAt time.Time
ExpiresAt time.Time
}
StepUpChallenge — выдаваемое испытание повышения: значение, запись сессии, которой оно выдано, и его срок.
func (StepUpChallenge) Validate ¶ added in v0.5.0
func (c StepUpChallenge) Validate() error
Validate — самопроверка выдаваемого испытания.
type StepUpChallengeState ¶ added in v0.5.0
type StepUpChallengeState int
StepUpChallengeState — почему испытание повышения не сгорело: три причины наружу различимы (приёмка SUA Р3), и каждая — свой отказ состояния.
const ( // StepUpChallengeNotIssued — строки этой пары (сессия, испытание) нет: не // выдавалось, выдано другой сессии либо сменено новой выдачей. StepUpChallengeNotIssued StepUpChallengeState = iota + 1 // StepUpChallengePresented — строка есть и уже предъявлена. StepUpChallengePresented // StepUpChallengeExpired — строка есть, не предъявлена, предел вышел. StepUpChallengeExpired )
type StructuralTuple ¶
type StructuralTuple struct {
// User — the SOURCE of the relation: the parent object ("account:<id>") for a
// parent-pointer, or the subject ("user:<id>") for the account owner.
User string
// Relation — the relation name on Object, named after the parent's type for a
// pointer ("account"/"project"/"cluster") or "owner".
Relation string
// Object — the object the relation is written on ("iam_access_binding:<id>").
Object string
}
StructuralTuple — one relation triple. Transport-neutral on purpose: the emitter converts it to its repo tuple type, and domain stays free of that type.
func AccountScopedStructuralFact ¶
func AccountScopedStructuralFact(accountID, objectType, objectID string) (StructuralTuple, bool)
AccountScopedStructuralFact is the one-line projection shared by the account-scoped iam types, whose cascade is `super_admin: admin from account`:
account:<accountID> → account → <objectType>:<objectID>
An empty accountID yields nothing: a row with no owning account (a system role) must not become reachable from any account's administrator.
type Subject ¶
type Subject struct {
Type SubjectType
ID SubjectID
}
Subject — one grantee of an AccessBinding (RBAC rules-model). A binding may carry 1..32 subjects; each yields an INDEPENDENT FGA tuple-set + emitted-tuple ledger lineage, so per-subject revoke/audit never touches another subject's tuples. A GROUP subject grants the role to every member (userset) — resolved to concrete principals by ExpandAccess and, for admin/editor-tier roles, requiring requireGrantAuthority on the scope (group-amplification guard).
func NormalizeSubjects ¶
func NormalizeSubjects(subjects []Subject, legacyType SubjectType, legacyID SubjectID) ([]Subject, error)
NormalizeSubjects resolves the canonical subjects[] set from the request input (two-way projection between the new subjects[] and the legacy single subject_type/subject_id):
- subjects[] is the canonical (preferred) input. When it is set, the legacy single pair (if also present) MUST equal subjects[0]; otherwise INVALID_ARGUMENT (a wire client cannot disagree with itself).
- When subjects[] is empty, the legacy single pair projects to a one-element subjects[] (legacy clients keep working).
- Empty subjects[] AND empty legacy single → INVALID_ARGUMENT ("Illegal argument subjects (must be 1..32)").
- More than 32 → INVALID_ARGUMENT (same text).
- A duplicate (type,id) → INVALID_ARGUMENT (the DB UNIQUE would also reject; fail sync with a clear message).
- Each subject is self-validated (closed type + non-empty id).
Pure domain — no DB / no transport. The use-case maps the returned error to the gRPC code (shared.MapValidationErr → INVALID_ARGUMENT).
func (Subject) IsGroup ¶
IsGroup reports whether this subject is a GROUP (userset) — the case that triggers the group-amplification guard and userset expansion.
func (Subject) IsSystemClass ¶ added in v0.5.0
IsSystemClass — субъект есть системный класс.
type SubjectID ¶
type SubjectID string
SubjectID — opaque id (user, service_account, or group). The paired SubjectType defines the semantics.
const ( // ClassAnonymous — любой вызывающий, в том числе без личности. ClassAnonymous SubjectID = "anonymous" // ClassAuthenticated — любой принципал кластера с личностью, из любого // аккаунта: выдача этому классу видна за границей арендатора (Р5). ClassAuthenticated SubjectID = "authenticated" )
Имена классов закрытого словаря.
type SubjectPrivilege ¶
type SubjectPrivilege struct {
BindingID AccessBindingID
RoleID RoleID
RoleName RoleName // resolved via JOIN; "" for a dangling/deleted role
ResourceType ResourceType
ResourceID string // opaque id (any prefix, cross-service OK)
Scope Scope // CLUSTER / ACCOUNT / PROJECT
Status AccessBindingStatus
CreatedAt time.Time
GrantedByUserID UserID
ExpiresAt *time.Time // nullable — TTL
// Derivation — DIRECT | GROUP. The zero value is treated as DIRECT by the
// transport projection (a row produced without an explicit derivation is a
// direct grant), so the proto enum never leaks UNSPECIFIED.
Derivation PrivilegeDerivation
// ViaGroupID — the group carrying the privilege when Derivation==GROUP; empty
// for a DIRECT grant. Without it a GROUP row is un-actionable: the binding's
// own subject is the group, so the administrator cannot tell which membership
// to remove.
ViaGroupID GroupID
}
SubjectPrivilege — enriched, public-safe projection of an AccessBinding for the subject-privileges view (RPC AccessBindingService.ListSubjectPrivileges).
It is an AccessBinding row JOINed with its Role so the human-readable RoleName is resolved server-side in ONE query (access_bindings ⋈ roles on role_id, same kaname schema, FK access_bindings_role_fk) — no per-row N+1 GetRole fan-out. A dangling role (deleted after a revoke) yields an empty RoleName; the consumer (UI) falls back to the raw RoleID (graceful).
Carries only tenant-facing, publicly-safe fields: id / role / scope / status / created_at / granted_by — никаких инфра-чувствительных данных и никаких condition/builtin_condition-internals (вне scope v1).
Derivation says HOW the subject holds the privilege: DIRECT (the binding names the subject itself) or GROUP (the binding names a group the subject belongs to, named by ViaGroupID). It is computed by the read query, not stored on the binding — a binding does not know which of its subjects' memberships a given reader is asking about.
type SubjectType ¶
type SubjectType string
SubjectType — enum: user|service_account|group|system_class.
const ( SubjectTypeUser SubjectType = "user" SubjectTypeServiceAccount SubjectType = "service_account" SubjectTypeGroup SubjectType = "group" )
SubjectType values.
const SubjectTypeSystemClass SubjectType = "system_class"
SubjectTypeSystemClass — вид субъекта «системный класс» (Р1).
func SubjectTypeOfPrincipal ¶ added in v0.7.0
func SubjectTypeOfPrincipal(t string) (SubjectType, bool)
SubjectTypeOfPrincipal — тип субъекта по виду принципала вызова. Единственная нормализация вида для сравнения «вызывающий — делегат» и «субъект — делегат» (условие CE-22 разбора классов): вид принципала пишется словом хранения ("service_account"), тип субъекта контракта — именем значения (`SUBJECT_TYPE_SERVICE_ACCOUNT`); обе формы приводятся к `SubjectType`.
func (SubjectType) Validate ¶
func (s SubjectType) Validate() error
Validate — SubjectType — enum check.
type SubordinateResource ¶
type SubordinateResource struct {
// Kind — двухчастный токен, которым подчинённый ресурс называется в видах
// учёта (`iam.user.credential` → `iam.credential`).
Kind LimitKind
// Parents — типы модели прав, от которых производен доступ. Их МОЖЕТ быть
// несколько: один класс сущности живёт в нескольких таблицах ровно тогда,
// когда видов родителя несколько.
Parents []LimitKind
// Tables — таблицы, чьи строки этот ресурс называет, в форме `схема.имя`.
// Это АНКЕР записи в дереве, а не справка.
Tables []string
// Why — почему своего типа модели прав нет. Запись без причины неотличима
// от записи без предмета.
Why string
}
SubordinateResource — ресурс, адресуемый арендатором и НЕ имеющий своего типа модели прав, потому что доступ к нему производен от РОДИТЕЛЯ.
Зачем понадобился второй источник имён ¶
Вид учёта обязан называть реальные типы модели прав — так гейт каталога и устроен, и обоснован он утверждением «модель прав знает КАЖДЫЙ адресуемый арендатором тип». На удостоверениях это утверждение ложно: они адресуются (`/iam/v1/users/{user_id}/tokens/{token_id}`), имеют свой идентификатор и свои глаголы, а типа модели прав у них нет — право на удостоверение вычисляется от человека (`token_issuer: subject`), и заводить объект, на который его можно было бы ВЫДАТЬ, модель намеренно избегает.
Почему это расширение, а не ослабление ¶
Множество допустимых имён остаётся закрытым и сверяемым с деревом. Разница с первой редакцией — в поле `Tables`: без него запись была бы САМОЗАЯВЛЕНИЕМ. У закрытой таблицы типов истинность анкерена гейтом дрейфа против канонической модели; здесь анкер — сами таблицы строк и стоящие на них триггеры списания (утверждения G5/G6 гейта каталога). Опечатка `iam.credentials` внутреннюю согласованность записи проходит и ловится ровно этим анкером.
func SubordinateResourceOf ¶
func SubordinateResourceOf(k LimitKind) (SubordinateResource, bool)
SubordinateResourceOf отдаёт запись подчинённого ресурса по его токену.
func SubordinateResources ¶
func SubordinateResources() []SubordinateResource
SubordinateResources отдаёт КОПИЮ перечня: перечень — предмет гейта, и вызывающий, способный его изменить, сделал бы гейт зависимым от порядка вызовов.
type SvcAccountName ¶
type SvcAccountName string
func (SvcAccountName) Validate ¶
func (n SvcAccountName) Validate() error
type TargetMember ¶
type TargetMember struct {
BindingID AccessBindingID
RoleID RoleID
RuleFP string
ObjectType string
ObjectID string
VerificationStatus VerificationStatus
}
TargetMember — one materialized member of a binding's target with its current verification status. object_type is a closed-table dotted key (e.g. "compute.instance"); object_id is an opaque cross-DB soft-ref.
RuleFP attributes the member to the (role) RULE that produced it: the content-hash of the ARM_LABELS rule (domain.Rule.Fingerprint) for a role.rules-driven member, or the sentinel "legacy-selector" for a legacy binding.selector member. Keying membership by rule_fp (not a positional index) lets a Role.Update that removes one rule eager-revoke ONLY that rule's members and lets the SAME object be a member under two different rules at different tiers (distinct rows).
type TargetTypeCatalog ¶ added in v0.6.0
type TargetTypeCatalog interface {
// FGAObjectType — имя типа модели для точечного имени каталога; ok=false у
// ресурса, чья строка снята либо которого в каталоге нет вовсе.
FGAObjectType(dotted string) (string, bool)
}
TargetTypeCatalog — ПОРТ живого каталога типов, каким его видит проверка цели выдачи: есть ли у платформы ЖИВАЯ строка каталога с таким точечным именем. Объявлен здесь, у потребителя; реализация — `catalog.Facts` (снимок живых строк). Порт узкий намеренно: цели нужен ровно один факт — есть ли строка.
type TrustedIssuer ¶
type TrustedIssuer struct {
// ClientID — НАША строка федеративного ключа, которую эта запись
// уполномочивает. Внешний субъект получает токен от её имени, никогда от
// своего.
ClientID string
// Issuer — идентификатор внешнего издателя, сверяемый с `iss` дословно.
Issuer string
// Subject — ТОЧНЫЙ субъект, сверяемый с `sub` дословно.
//
// Точный, а не образец: доверие образцу означало бы, что запись покрывает
// субъектов, которых называющий её не перечислял, — и узнать их состав
// нельзя ни по записи, ни по журналу.
Subject string
// PublicKeyPEM — открытый ключ ИЗДАТЕЛЯ в форме SPKI PEM.
PublicKeyPEM string
// Algorithm — зарегистрированный алгоритм издателя. Пустое значение
// означает «ключа нет», а НЕ «любой алгоритм».
Algorithm string
// ExpiresAt — момент истечения ДОВЕРИЯ в Unix-секундах; 0 означает
// «бессрочно».
ExpiresAt int64
}
TrustedIssuer — одна запись НАШЕГО перечня доверенных издателей.
Форма повторяет то, что прежде хранил у себя поставщик: издатель, ТОЧНЫЙ субъект, ключевой материал издателя и срок. Повторяет намеренно — перевод перечня к нам не должен был заодно менять то, о чём перечень.
func (TrustedIssuer) CanVouch ¶
func (ti TrustedIssuer) CanVouch() bool
CanVouch отвечает, способна ли запись вообще служить основанием доверия.
Тот же класс, что `AssertionClient.CanPresentAssertion`: пустой ключ и пустой алгоритм — это «ключа нет». Запись без ключа не отвергала бы ничего, а принимала бы всё, что называет её пару, — то есть доверие издателю выродилось бы в доверие строке таблицы.
type TrustedSubject ¶
type TrustedSubject struct {
Issuer string
SubjectPattern string
// PublicKeyPEM — открытый ключ ИЗДАТЕЛЯ (SPKI PEM). Тот, которым подписано
// внешнее утверждение; нашего ключевого материала федеративная строка не
// несёт вовсе.
PublicKeyPEM string
// KeyAlgorithm — зарегистрированный алгоритм издателя. Пустое значение
// означает «ключа нет», а НЕ «любой алгоритм».
KeyAlgorithm string
}
TrustedSubject — one (issuer, subject) tuple permitted to assert a federated ServiceAccountOAuthClient. `Issuer` MUST match the external OIDC `iss` claim verbatim; `SubjectPattern` is a LITERAL-anchored exact subject (`^<literal>$`, no regex metacharacters).
Точная форма субъекта требуется потому, что доверие выдаётся ПОИМЁННО: запись, покрывающая субъектов образцом, называет тех, кого выдававший не перечислял, и установить их состав нельзя ни по записи, ни по журналу.
Перечень — НАША таблица (задача kacho#1124) ¶
Прежде решение о доверии принимал поставщик: запись жила у него, и там же лежал ключ издателя. Отсюда была выведена и прежняя редакция этого комментария — «служба прав вне пути запроса, поэтому образец было бы нечем применить». Сегодня служба прав НА пути запроса: перечень читает её проверка утверждения (`internal/clientassertion`, федеративная полоса). Точная форма осталась, но держит её теперь названный выше довод, а не чужая реализация.
func (TrustedSubject) LiteralSubject ¶
func (ts TrustedSubject) LiteralSubject() (string, bool)
LiteralSubject returns the exact subject enclosed by a valid literal-anchored pattern (`^<literal>$` → `<literal>`), and false when the pattern is not a literal-anchored subject (wildcard / unanchored / regex metacharacters).
func (TrustedSubject) Validate ¶
func (ts TrustedSubject) Validate() error
Validate — Issuer must be an https URL to a public host (anti-SSRF on the trust-config: no non-https / loopback / private / link-local host); SubjectPattern must be a literal-anchored exact subject. Length caps mirror the proto (≤512 each).
Ключевой материал издателя обязателен и проверяется на РАЗБИРАЕМОСТЬ здесь, а не при первом предъявлении: непригодный ключ, принятый на выдаче, даёт запись доверия, которая не примет никогда никого, — то есть возможность, объявленную и не работающую ни при каком входе. Отказ на выдаче виден тому, кто её заказал; отказ на предъявлении виден постороннему и неотличим для него от «доверия нет».
type TypeVerbLookup ¶
TypeVerbLookup — набор глаголов, объявленный типом (module, resource); ok=false, когда пара не резолвится ни в один известный тип.
Приходит ПАРАМЕТРОМ, а не импортом таблицы: владельцем таблицы остаётся authzmap, а домен — без внешних зависимостей (см. rule_verbs.go, «pure domain»).
func WithCommonFallback ¶
func WithCommonFallback(lookup TypeVerbLookup, common []string) TypeVerbLookup
WithCommonFallback оборачивает lookup так, что пара-ПОДСТАНОВКА получает словарь, ОБЩИЙ для всех ресурсов.
Это решение ВЫЗЫВАЮЩЕГО, не домена: правило-подстановка (`*.*` роли- суперпользователя) своего набора не имеет by construction — перечислить ресурсы подстановки домену нечем, каталог ему не принадлежит, — а пустое превью читалось бы как «роль ничего не даёт».
Запасной словарь даётся ПОДСТАНОВКЕ, а не всякому промаху (kacho#1814) ¶
Прежде его получала ЛЮБАЯ нерезолвящаяся пара, и это переворачивало смысл снятия ресурса: правило, называющее снятый `compute.disk`, разворачивалось в глаголы ВСЕЙ платформы, то есть после снятия превью показывало не «меньше», а БОЛЬШЕ. Роль обещала арендатору то, чего материализация не даёт, и обещала тем громче, чем уже правило.
Различает ФОРМА пары, а не исход резолва: подстановка называет «все» и потому законно берёт общий словарь; названный ресурс называет ОДИН тип, и если этого типа нет — давать нечего. Промах названной пары остаётся промахом (`ok=false`), и вызывающий разворачивает её ни во что.
type User ¶
type User struct {
ID UserID
AccountID AccountID
ExternalID ExternalSubject
Email Email
DisplayName DisplayName
InviteStatus InviteStatus
InvitedBy UserID // user.id of admin who invoked Invite; "" if self-signup
CreatedAt time.Time
// InviteExpiresAt — срок строки приглашения (ID-MAIL-1, MAIL-23): после
// него активация отвергается. Нулевое время — «срок не назначен»: так
// выглядят строки, заведённые до появления колонки, и выкупленные строки,
// у которых срок предмета не имеет.
InviteExpiresAt time.Time
// Labels — tenant-facing метки. Делают User label-selectable наравне с
// account/project: ARM_LABELS-грант на iam.user материализует v_list по
// `labels @> matchLabels`, а List фильтрует через viewer ∪ v_list.
Labels Labels
}
User — зеркало личности из внешнего провайдера. Одна личность — ОДНА строка, сколько бы аккаунтов её ни пригласило.
Принадлежность аккаунту здесь НЕ живёт: её выражает строка `kaname.memberships` (одна на пару «человек × аккаунт»), и членств у одного человека бывает несколько. Ключ идентичности — глобальный: `users_identity_email_uniq` и `users_identity_external_id_uniq` (`20260823050000_users_identity_uniqueness_goes_global.sql`, стадия S4-expand перехода IAM-ID-1). PENDING-строка держит external_id="" до первого входа.
Поле AccountID — ЛЕГАСИ-колонка перехода: она жива и `NOT NULL` до стадии S4, но «его аккаунт» из неё не читается — у человека их несколько.
func (User) InviteExpired ¶ added in v0.5.0
InviteExpired — истёк ли срок приглашения к моменту now. Строка без срока не истекает никогда — это законное состояние, а не отказ.
type UserOAuthClient ¶
type UserOAuthClient struct {
ID UserOAuthClientID
UserID UserID
Description Description
CreatedByUserID UserID
CreatedAt time.Time
ExpiresAt *time.Time
LastUsedAt *time.Time
// PublicKeyPEM — SPKI-encoded ECDSA P-256 публичный ключ удостоверения.
// По нему проверяется подпись `client_assertion` на пути выдачи токена.
PublicKeyPEM string
// KeyAlgorithm — JOSE alg зарегистрированного ключа. Всегда "ES256" для новых
// токенов.
KeyAlgorithm string
// CredentialKind — вид удостоверения. ЗАПИСЫВАЕТСЯ при вставке; читателем
// не вычисляется и из состава прочих полей не выводится.
CredentialKind CredentialKind
// SecretHash — sha256 по идентификатору строки И секретной части вместе,
// 32 байта. Непуст ТОЛЬКО у вида SECRET. Сам секрет не хранится нигде: он
// существует только в теле ответа, полученного вызывающим выдачи.
SecretHash []byte
// Name — человекочитаемое имя токена, выставляется на Issue (create-only,
// immutable — ресурс несёт только Issue/List/Revoke). Пусто для legacy-строк.
Name OAuthClientName
// Labels — произвольные метки токена, выставляются на Issue (create-only,
// immutable). Пусто для legacy-строк.
Labels Labels
}
UserOAuthClient — персональный access-токен пользователя.
private_key_jwt: kaname генерирует пару ключей ECDSA P-256 на каждый токен, держит SPKI public PEM плюс алгоритм и возвращает приватный PEM вызывающему ровно один раз. Секрет не существует at-rest.
Клиентом это удостоверение называется по идентификатору ЭТОЙ строки: им подписывается `client_assertion`, и по нему же разрешает клиента наш реестр утверждений. Второго имени у него нет.
N:1 — у одного User может быть несколько токенов.
func (UserOAuthClient) Validate ¶
func (c UserOAuthClient) Validate() error
Validate — self-validating инвариант доменной сущности.
type UserOAuthClientID ¶
type UserOAuthClientID string
UserOAuthClientID — новый формат `uoc<17-crockford>` (corelib `ids.NewID`, без подчёркивания). id существующих строк immutable, поэтому валидатор принимает и legacy `uoc_<17-crockford>`.
func (UserOAuthClientID) Validate ¶
func (id UserOAuthClientID) Validate() error
type UserTokenRevocation ¶
type UserTokenRevocation struct {
UserID UserID
RevokeBefore time.Time
Reason string
RevokedBy UserID
}
UserTokenRevocation — per-user "revoke-all-before" cutoff (migration 0012). Backs admin ForceLogout + Revoke(revoke_all_user_tokens): any token whose originating session authenticated at or before RevokeBefore is denied at refresh. One row per user (PK user_id); the cutoff only ever advances (monotonic GREATEST upsert at the repo layer) so a re-auth past the cutoff is allowed again (no permanent lockout).
func (UserTokenRevocation) Validate ¶
func (u UserTokenRevocation) Validate() error
Validate — self-validating domain entity.
type VerificationCode ¶ added in v0.5.0
type VerificationCode struct {
ID VerificationCodeID
UserID UserID
// Email — значение адреса, на которое код выдан.
Email Email
Digest CodeDigest
// IssuedAt — момент выдачи (часы полосы); по нему считаются письма (Р9).
IssuedAt time.Time
// ExpiresAt — абсолютный срок; величина — настройка.
ExpiresAt time.Time
// Attempts — сколько неподошедших предъявлений засчитано коду.
Attempts int
}
VerificationCode — запись кода подтверждения (Р7). Состав закрыт.
func (VerificationCode) Validate ¶ added in v0.5.0
func (c VerificationCode) Validate() error
Validate — запись, годная к выдаче: судится до базы то, что база держит ограничениями.
type VerificationCodeID ¶ added in v0.5.0
type VerificationCodeID string
VerificationCodeID — идентификатор строки кода подтверждения. Наружу не адресуется.
type VerificationCodeValue ¶ added in v0.5.0
type VerificationCodeValue = RecoveryCodeValue
VerificationCodeValue — значение кода подтверждения: тип кода-предъявителя Ф5 Р1.
func NewVerificationCodeValue ¶ added in v0.5.0
func NewVerificationCodeValue() (VerificationCodeValue, error)
NewVerificationCodeValue — свежий код из криптографического источника.
func PresentedVerificationCode ¶ added in v0.5.0
func PresentedVerificationCode(value string) VerificationCodeValue
PresentedVerificationCode — код, как его прислал человек: регистр, разделители и знаки-двойники не значимы. Пустое после приведения — отсутствие кода.
type VerificationStatus ¶
type VerificationStatus string
VerificationStatus — the observable per-member containment verdict.
const ( // VerificationPending — the object is NOT yet in resource_mirror (the grant // raced ahead of the owner's RegisterResource). No FGA tuple is emitted; the // reconciler verifies it when the mirror row arrives. VerificationPending VerificationStatus = "PENDING_VERIFICATION" // VerificationActive — the object is in resource_mirror AND under the // binding's scope-anchor. The per-object FGA tuple IS emitted. VerificationActive VerificationStatus = "ACTIVE" // VerificationRejected — the object is in resource_mirror but NOT under scope // (mirror.parent_* ⋢ scope). No tuple; an audit event is written (not silent). VerificationRejected VerificationStatus = "REJECTED" )
type WithdrawnGrant ¶
type WithdrawnGrant struct {
// ObjectType — точечный тип объекта, чью строку каталога сняли.
ObjectType string
// Verb — глагол. ПУСТАЯ строка есть ЯКОРЬ объявления правила, а не «любой
// глагол»: у объявления, не назвавшего глагол поимённо, его нет.
Verb string
// Source — из какой проекции переселена строка.
Source WithdrawnGrantSource
// Reason — причина снятия строки каталога, записанная применителем
// манифеста. Причина ПЛАТФОРМЫ, а не действие арендатора.
Reason string
// WithdrawnAt — момент переселения. Время ТРАНЗАКЦИИ применения: у всего,
// что отобрано одним применением, он совпадает дословно.
WithdrawnAt time.Time
// AppliedBy — АВТОР применения, снявшего эту строку (kacho#2005): проверенная
// личность вызывающего на пути глагола либо названный процессный актор на
// пути старта. НЕ учётка, под которой исполнялась транзакция.
//
// Для разбора «почему у меня отобрали право» это второй по важности вопрос
// после «почему»: причина называет, ЧТО случилось с платформой, а автор — КТО
// это сделал.
//
// ПУСТАЯ строка означает «строка переселена ДО заведения колонки», а не
// «автора потеряли»: восстановить его у таких строк не из чего.
AppliedBy string
// Cause — ПОЧЕМУ строка переселена (kacho#1913). Различие наблюдаемо, потому что
// от него зависит поведение: оживление роли снимает строки причины
// [WithdrawnGrantCauseRoleRetired] и не трогает строк причины
// [WithdrawnGrantCauseCatalogRetired].
//
// Отличается от [WithdrawnGrant.Source] предметом: тот называет, ИЗ КАКОЙ
// проекции строка переселена, этот — ПОЧЕМУ. Величины независимы: каждая
// причина встречается у обеих популяций.
Cause WithdrawnGrantCause
}
WithdrawnGrant — одна проекция правила роли, потерявшая референт при снятии строки каталога платформы и ПЕРЕСЕЛЁННАЯ в ведомость, а не отобранная молча.
Зачем этот тип, когда рядом уже стоят счётчики ¶
RoleIntegrity отвечает СКОЛЬКО сегментов перестало проецироваться и не отвечает, ЧТО именно и почему. Со стороны арендатора «право отобрали» без этого неотличимо от «права не было»: свою роль он не менял, а действовать она перестала.
Он ОБЪЯСНЯЕТ состояние и не определяет его ¶
Целость выводится из того, что читает вердикт, а не из ведомости: у роли, пострадавшей вторым путём (тип объявлен, а модель прав его не знает), переселения не было ВОВСЕ, и ведомость о ней пуста при нездоровом состоянии. Судить целость по ведомости значило бы читать такую роль здоровой — форма инцидента 513001.
type WithdrawnGrantCause ¶
type WithdrawnGrantCause uint8
WithdrawnGrantCause — почему проекция переселена в ведомость.
Нулевой вариант означает «этим ответом не вычислено» и НИКОГДА «причина неизвестна»: непонятая строка ведомости отвергается разбором, а не выдаётся за невычисленную.
const ( // WithdrawnGrantCauseUnknown — не вычислено ЭТИМ ответом. WithdrawnGrantCauseUnknown WithdrawnGrantCause = iota // WithdrawnGrantCauseCatalogRetired — снята строка КАТАЛОГА платформы, на // которую ссылалось правило. Роль при этом объявлена и жива. WithdrawnGrantCauseCatalogRetired // WithdrawnGrantCauseRoleRetired — снята САМА РОЛЬ. Возврат объявления // оживляет её и снимает эти строки. WithdrawnGrantCauseRoleRetired )
type WithdrawnGrantSource ¶
type WithdrawnGrantSource uint8
WithdrawnGrantSource — из какой проекции правила переселена строка ведомости.
Популяции разделены намеренно: «право отобрано» и «правило перестало резолвиться» — разные события для того, кто разбирает последствия, и сложив их, мы потеряли бы именно это различие.
const ( // WithdrawnGrantSourceUnknown — популяция не прочитана ЭТИМ ответом. Никогда // не означает «популяция неизвестна»: ведомость несёт её всегда, закрытым // набором на уровне схемы. WithdrawnGrantSourceUnknown WithdrawnGrantSource = iota // WithdrawnGrantSourceGrant — выдача глагола: право по паре действовало и // перестало. WithdrawnGrantSourceGrant // WithdrawnGrantSourceRuleRef — объявление правила: сегмент перестал // резолвиться в каталог. WithdrawnGrantSourceRuleRef )
Source Files
¶
- access_binding.go
- access_binding_scope.go
- access_binding_target.go
- access_key.go
- access_key_login.go
- account.go
- audit_outbox_entry.go
- basic_credential_refusal.go
- ceremony_handle.go
- client_assertion.go
- client_description.go
- cluster.go
- cluster_admin_grant.go
- constants.go
- constants_extended.go
- credential_ceiling.go
- credential_kind.go
- derived_id.go
- email_change_code.go
- email_verification_code.go
- federated_assertion.go
- feed_registry.go
- grant_delegation.go
- group.go
- human_session.go
- ids_extended.go
- interactive_client.go
- limit.go
- limit_posture_stated.go
- login_method.go
- membership.go
- module_set.go
- nested_rule.go
- oauth_ceremony.go
- oauth_ceremony_family_bound.go
- object_type_name.go
- own_lane_subject.go
- password_cost_class.go
- password_hash_format.go
- personal_account.go
- principal_claims.go
- project.go
- public_read.go
- recovery_code.go
- recovery_completion.go
- retired_types.go
- role.go
- role_catalog.go
- role_cluster_admin.go
- role_computed_state.go
- role_definition_tier.go
- role_effective_verbs.go
- role_integrity.go
- role_lifecycle.go
- role_rule_state.go
- role_scope.go
- rule.go
- rule_codec.go
- rule_fingerprint.go
- rule_policy.go
- rule_unregistered_name.go
- rule_verbs.go
- seed_identity_window.go
- seeded_ids.go
- selector_feed.go
- service_account.go
- service_account_oauth_client.go
- session_revocation.go
- signing_key.go
- status.go
- step_up_challenge.go
- structural_tuple.go
- subject.go
- subject_privilege.go
- system_class.go
- target_membership.go
- types.go
- user.go
- user_oauth_client.go
- user_token_revocation.go