Documentation
¶
Overview ¶
Package authorize — AuthorizeService gRPC handler. Thin transport-layer wrapper around the service.AuthorizeService use-case.
Subject binding: the handler accepts `subject` directly from the protobuf request, and decides HERE who may name a subject other than themselves — see caller_authority.go. The proto comment claiming the gateway gates this on `iam.subjects.checkAuthorization` describes a permission that exists in no catalog and a relation that exists in no model; the catalog entry these RPCs actually carry is answered by every authenticated subject.
whoami.go — WhoAmIUseCase: aggregate caller identity + permission snapshot for `AuthorizeService.WhoAmI` (GET /iam/v1/me).
Pipeline (per request, no caching — each call hits FGA twice + Postgres once per account row; cheap enough for UI bootstrap):
- Resolve principal from auth context (operations.PrincipalFromContext). Anonymous / system-bootstrap → Unauthenticated.
- (user only) Read user row from Postgres for email + display_name.
- FGA Check `system_admin@cluster:cluster_root`.
- FGA Check `viewer@cluster:cluster_root`.
- Reader.Users().ListAccountsForUser → set of account ids.
- For each account: Reader.Accounts().Get + AccessBindings().ListBySubject (filtered to account-scope) → coarse role tags.
Designed for UI bootstrap and CLI permission previews — the data is coarse (cluster flags + per-account role tags) and intentionally NOT a per-RPC authority. Per-RPC gating remains at the api-gateway authz middleware.
Index ¶
- type Authorizer
- type Handler
- func (h *Handler) BatchCheck(ctx context.Context, req *iamv1.BatchAuthorizeCheckRequest) (*iamv1.BatchAuthorizeCheckResponse, error)
- func (h *Handler) Check(ctx context.Context, req *iamv1.AuthorizeCheckRequest) (*iamv1.AuthorizeCheckResponse, error)
- func (h *Handler) ExpandRelations(ctx context.Context, req *iamv1.ExpandRelationsRequest) (*iamv1.ExpandRelationsResponse, error)
- func (h *Handler) ListSubjects(ctx context.Context, req *iamv1.ListSubjectsRequest) (*iamv1.ListSubjectsResponse, error)
- func (h *Handler) WhoAmI(ctx context.Context, _ *iamv1.WhoAmIRequest) (*iamv1.WhoAmIResponse, error)
- func (h *Handler) WithCallerAuthority(checker authzguard.RelationChecker) *Handler
- func (h *Handler) WithInsecureAnonymousPeer(insecure bool) *Handler
- type WhoAmIAccountMembership
- type WhoAmICheckerForAuthz
- type WhoAmIResult
- type WhoAmIUseCase
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Authorizer ¶
type Authorizer interface {
Check(ctx context.Context, req service.CheckRequest) (*service.CheckResult, error)
BatchCheck(ctx context.Context, reqs []service.CheckRequest) ([]*service.CheckResult, error)
ListSubjects(ctx context.Context, req service.ListSubjectsRequest) (*service.ListSubjectsResult, error)
ExpandRelations(ctx context.Context, req service.ExpandRequest) (*service.ExpandResult, error)
}
Authorizer — порт решателя, которым пользуется ЭТОТ транспорт.
Интерфейс, а не конкретный use-case, ровно по одной причине: между транспортом и решателем встаёт наблюдатель полос (`observability/metrics.InstrumentedSubjectAuthorizer`), и без порта его некуда поставить, не втащив сбор величин в use-case. Пока порта не было, полоса КРАЯ не наблюдалась вовсе: счётчик владельца прав видел только пообъектное звено модулей, и всякое «проверок в секунду», снятое с него, было занижено — на пути чтения по идентификатору ровно вдвое.
Набор методов — тот, который зовёт транспорт, и ни одним больше: порт шире употребления обязывал бы всякого будущего дублёра реализовывать то, чего у него не спрашивают.
type Handler ¶
type Handler struct {
iamv1.UnimplementedAuthorizeServiceServer
// contains filtered or unexported fields
}
Handler — gRPC server.
func NewHandler ¶
func NewHandler(svc Authorizer, whoAmI *WhoAmIUseCase) *Handler
NewHandler — builder. Both svc and whoAmI are required (composition root wires both unconditionally; nil at construction time means a wiring bug).
func (*Handler) BatchCheck ¶
func (h *Handler) BatchCheck(ctx context.Context, req *iamv1.BatchAuthorizeCheckRequest) (*iamv1.BatchAuthorizeCheckResponse, error)
BatchCheck — see iamv1.AuthorizeServiceServer.
func (*Handler) Check ¶
func (h *Handler) Check(ctx context.Context, req *iamv1.AuthorizeCheckRequest) (*iamv1.AuthorizeCheckResponse, error)
Check — see iamv1.AuthorizeServiceServer.
func (*Handler) ExpandRelations ¶
func (h *Handler) ExpandRelations(ctx context.Context, req *iamv1.ExpandRelationsRequest) (*iamv1.ExpandRelationsResponse, error)
ExpandRelations — see iamv1.AuthorizeServiceServer.
func (*Handler) ListSubjects ¶
func (h *Handler) ListSubjects(ctx context.Context, req *iamv1.ListSubjectsRequest) (*iamv1.ListSubjectsResponse, error)
ListSubjects — see iamv1.AuthorizeServiceServer.
func (*Handler) WhoAmI ¶
func (h *Handler) WhoAmI(ctx context.Context, _ *iamv1.WhoAmIRequest) (*iamv1.WhoAmIResponse, error)
WhoAmI — see iamv1.AuthorizeServiceServer. Marshals the WhoAmI use-case result into the proto response shape. The use-case is the authoritative gate (anonymous → Unauthenticated); the handler is the thin transport wrapper.
func (*Handler) WithCallerAuthority ¶
func (h *Handler) WithCallerAuthority(checker authzguard.RelationChecker) *Handler
WithCallerAuthority injects the FGA relation checker used by the inner caller-authority defense-in-depth gate. Returns the receiver for chaining.
func (*Handler) WithInsecureAnonymousPeer ¶
WithInsecureAnonymousPeer opts a stand WITHOUT mTLS into the permissive treatment of an anonymous/system principal that carries no verified module cert. Passing false (or never calling this at all) keeps the fail-closed default. The composition root derives it from the AuthN mode: WithInsecureAnonymousPeer(!cfg.AuthN.Mode.IsProduction()). Returns the receiver for chaining.
type WhoAmIAccountMembership ¶
type WhoAmIAccountMembership struct {
AccountID domain.AccountID
AccountName string
Roles []string // coarse: owner | admin | editor | viewer
}
WhoAmIAccountMembership — per-account membership snapshot returned by WhoAmI; coarse role tags derived from ACTIVE AccessBindings on the account object. Mirrors iamv1.AccountMembership (handler layer marshals).
type WhoAmICheckerForAuthz ¶
type WhoAmICheckerForAuthz interface {
CheckWithContext(ctx context.Context, subject, relation, object string, condCtx map[string]any) (bool, error)
}
WhoAmICheckerForAuthz — minimal FGA port-iface for WhoAmI's two cluster Check calls (system_admin, viewer). Narrower than the full service.Authorizer so unit tests can mock just two methods.
type WhoAmIResult ¶
type WhoAmIResult struct {
Subject string // "user:<id>" / "service_account:<id>"
UserID domain.UserID
Email string
DisplayName string
SystemAdmin bool
ClusterViewer bool
Accounts []WhoAmIAccountMembership
CheckedAt time.Time
}
WhoAmIResult — output.
type WhoAmIUseCase ¶
type WhoAmIUseCase struct {
// contains filtered or unexported fields
}
WhoAmIUseCase — aggregate identity + permission snapshot.
func NewWhoAmIUseCase ¶
func NewWhoAmIUseCase(r kanamerepo.Repository, relations WhoAmICheckerForAuthz) *WhoAmIUseCase
NewWhoAmIUseCase — builder.
func (*WhoAmIUseCase) Execute ¶
func (u *WhoAmIUseCase) Execute(ctx context.Context) (*WhoAmIResult, error)
Execute — see package doc.