interactiveclient

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: AGPL-3.0 Imports: 20 Imported by: 0

Documentation

Overview

Package interactiveclient — use-cases of InternalInteractiveClientService (IAM-INT-1): the lifecycle of the OAuth2 client through which a HUMAN completes an interactive sign-in ceremony.

Clean Architecture: this package defines the narrow ports below and depends on nothing but domain + the corelib operation envelope. Concrete adapters (pgx, the identity provider's admin API) live in internal/repo and internal/clients and are wired in cmd/kaname/wiring.go.

Index

Constants

This section is empty.

Variables

View Source
var ErrClientSecretNotSerializable = errors.New("interactive client secret is not serializable")

ErrClientSecretNotSerializable — носитель не сериализуется. Отказ ГРОМКИЙ: пустое, выданное молча, было бы ложью «секрета нет».

Functions

This section is empty.

Types

type ClientSecret added in v0.5.0

type ClientSecret struct {
	// contains filtered or unexported fields
}

ClientSecret — секрет конфиденциального клиента, выданный реестром. Нулевое значение — секрета нет (клиент со способом `none`).

func NewClientSecret added in v0.5.0

func NewClientSecret(value string) (ClientSecret, error)

NewClientSecret — секрет, отчеканенный реестром, как есть. Пустой отвергается: отсутствие секрета выражается нулевым носителем, а не пустым значением в непустом.

func (ClientSecret) Format added in v0.5.0

func (s ClientSecret) Format(f fmt.State, verb rune)

Format — заглушка на ЛЮБОМ глаголе форматирования, включая `%x` и `%q`.

func (ClientSecret) GoString added in v0.5.0

func (s ClientSecret) GoString() string

GoString — заглушка и для `%#v`.

func (ClientSecret) IntoResponse added in v0.5.0

func (s ClientSecret) IntoResponse(resp *iamv1.CreateInteractiveClientResponse)

IntoResponse — ЕДИНСТВЕННЫЙ выход значения: заполнение поля `client_secret` ответа вызова `Create`. Нулевой носитель поле не трогает.

func (ClientSecret) IsZero added in v0.5.0

func (s ClientSecret) IsZero() bool

IsZero — секрета нет.

func (ClientSecret) LogValue added in v0.5.0

func (s ClientSecret) LogValue() slog.Value

LogValue — заглушка для журнала.

func (ClientSecret) MarshalJSON added in v0.5.0

func (s ClientSecret) MarshalJSON() ([]byte, error)

MarshalJSON — отказ.

func (ClientSecret) MarshalText added in v0.5.0

func (s ClientSecret) MarshalText() ([]byte, error)

MarshalText — отказ.

func (ClientSecret) String added in v0.5.0

func (s ClientSecret) String() string

String — заглушка.

type CreateUseCase

type CreateUseCase struct {
	// contains filtered or unexported fields
}

CreateUseCase — registers the client at the service's own registry, then records it. The client is confidential and its secret is shown once, in the answer of this call (kaname#405).

func NewCreateUseCase

func NewCreateUseCase(r clientRepo, p ProviderClients, ops operations.Repo, audiences []string, logger *slog.Logger) *CreateUseCase

NewCreateUseCase — constructor. `audiences` comes from iam's own configuration (Р2): the caller never supplies it and it is echoed output-only.

func (*CreateUseCase) Execute

Execute — validate → persist Operation → register at the registry → check the secret triple → insert the row with the verification value → split the response bodies (stored without the secret, shown with it).

WHY THE PROVIDER IS CONTACTED BEFORE THE ROW IS WRITTEN, AND WHAT PAYS FOR IT. The row must carry the provider's client id, so registration comes first. If the insert then fails — most often because the name is taken — the client that was just registered is deregistered again. Without that compensation an orphan would remain at the provider that the platform has no record of and no way to name: nothing would ever remove it, and it would keep accepting ceremonies.

The Operation row is persisted BEFORE either side effect, so the id the caller receives is always pollable; a failure is recorded on it as a terminal error rather than leaving the caller polling a row that does not exist.

type DeleteUseCase

type DeleteUseCase struct {
	// contains filtered or unexported fields
}

DeleteUseCase — removes the client at the provider and its row.

func NewDeleteUseCase

func NewDeleteUseCase(r clientRepo, p ProviderClients, ops operations.Repo, logger *slog.Logger) *DeleteUseCase

NewDeleteUseCase — constructor.

func (*DeleteUseCase) Execute

func (uc *DeleteUseCase) Execute(ctx context.Context, id string) (*operationpb.Operation, error)

Execute — idempotent by construction.

Deleting an already-absent client succeeds and reports the same code as the first delete: the caller asked for the client to be gone, and it is. The row is removed first and the provider-side deregistration follows only when a row was actually taken — otherwise a repeated Delete would keep asking the provider to remove something already removed.

type GetUseCase

type GetUseCase struct {
	// contains filtered or unexported fields
}

GetUseCase — sync read of one client.

func NewGetUseCase

func NewGetUseCase(r clientRepo) *GetUseCase

NewGetUseCase — constructor.

func (*GetUseCase) Execute

func (uc *GetUseCase) Execute(ctx context.Context, id string) (domain.InteractiveClient, error)

Execute — direct-read lane: a well-formed id with no row is NOT_FOUND.

type Handler

type Handler struct {
	iamv1.UnimplementedInternalInteractiveClientServiceServer
	// contains filtered or unexported fields
}

Handler implements iamv1.InternalInteractiveClientServiceServer.

func NewHandler

NewHandler assembles the Handler. Composition root: cmd/kaname/wiring.go.

func (*Handler) Create

Create — async; returns the Operation envelope.

func (*Handler) Delete

Delete — async; returns the Operation envelope.

func (*Handler) Get

Get — sync read of one client.

func (*Handler) List

List — sync cursor-paginated read.

func (*Handler) Update

Update — async; returns the Operation envelope.

type ListResult

type ListResult struct {
	Clients       []domain.InteractiveClient
	NextPageToken string
}

ListResult — one page plus the cursor for the next.

type ListUseCase

type ListUseCase struct {
	// contains filtered or unexported fields
}

ListUseCase — sync cursor-paginated read.

func NewListUseCase

func NewListUseCase(r clientRepo) *ListUseCase

NewListUseCase — constructor.

func (*ListUseCase) Execute

func (uc *ListUseCase) Execute(ctx context.Context, pageSize int64, pageToken, filter string) (ListResult, error)

Execute — pagination format is validated FIRST, so a garbage token or an out-of-range page size is INVALID_ARGUMENT independently of what the caller holds and of what the store contains. `page_size` out of range is REJECTED, never clamped: silent clamping returns a different page than the one asked for and the caller never learns of it.

type ProviderClient

type ProviderClient struct {
	ClientID                string
	GrantTypes              []string
	TokenEndpointAuthMethod string
	Audiences               []string
	// Secret — the secret, minted by the registry, shown once in the answer of
	// the call. A carrier and not a string: see client_secret.go.
	Secret ClientSecret
	// SecretVerifier — the verification value of Secret; it is what the row
	// keeps. Zero — no secret.
	SecretVerifier domain.LoginVerifier
}

ProviderClient — what the provider gives back.

The triple «method ⟺ material ⟺ secret» must agree: a secret method (`client_secret_basic`, `client_secret_post`) comes with BOTH a secret and its verification value, `none` with neither. The use-case refuses any other combination before the row is written (`secretMaterialAgrees`): the schema holds only one direction of it on purpose (a client of an external provider has no material in our row).

type ProviderClientSpec

type ProviderClientSpec struct {
	Name                   string
	RedirectURIs           []string
	PostLogoutRedirectURIs []string
	Audiences              []string
	// GrantTypes — decided by the use-case, forwarded verbatim. The adapter does
	// not choose: this resource exists to register exactly one shape, and a
	// default living in the adapter is how the other three registration paths
	// ended up all meaning `client_credentials`.
	GrantTypes []string
}

ProviderClientSpec — what iam asks the provider to register. The caller of the RPC supplies only the first two fields' worth of intent; everything else is iam's decision (Р2 — the audience is stamped, never accepted).

type ProviderClients added in v0.5.0

type ProviderClients interface {
	Register(ctx context.Context, in ProviderClientSpec) (ProviderClient, error)
	Deregister(ctx context.Context, providerClientID string) error
}

ProviderClients — порт заведения и снятия клиента интерактивного входа.

WHY IT IS A PORT AND NOT A DIRECT CALL. iam is the single facade to the provider (core rule #16); expressing the dependency here keeps the use-case testable without a live provider and keeps the provider's HTTP shape out of the business layer.

ИМЕНОВАН НАРУЖУ, и это не косметика: исполнитель собирается композиционным корнем, а не use-case, — наш собственный реестр (`*pg.OwnInteractiveClientProvider`). Прежде исполнителей было два и выбирала их посадка (kaname#313); зеркало чужого реестра снято вместе с посадкой внешнего поставщика (kaname#363), а порт остался границей: бизнес-слой не знает, чем исполняется заведение клиента.

type UpdateUseCase

type UpdateUseCase struct {
	// contains filtered or unexported fields
}

UpdateUseCase — applies the mutable fields.

func NewUpdateUseCase

func NewUpdateUseCase(r clientRepo, ops operations.Repo, logger *slog.Logger) *UpdateUseCase

NewUpdateUseCase — constructor.

func (*UpdateUseCase) Execute

Execute — mask discipline, then the write.

Jump to

Keyboard shortcuts

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