context

package
v0.0.0-...-d09923a Latest Latest
Warning

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

Go to latest
Published: Nov 5, 2026 License: LGPL-2.1 Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// PrincipalKindUser is a person acting through a client.
	PrincipalKindUser = PrincipalKind("user")
	// PrincipalKindService is a workload acting on the system's behalf: a scheduled job, a
	// message consumer, a device. It authorizes exactly like a user, on entitlements it was
	// granted - never by virtue of being internal.
	PrincipalKindService = PrincipalKind("service")
	// PrincipalKindSystem is reserved for future platform-level operations. It deliberately
	// carries NO bypass behaviour; treat it as unauthorized until that is designed.
	PrincipalKindSystem = PrincipalKind("system")
)
View Source
const (
	RequestSourceHttp   = RequestSource("http")
	RequestSourceJob    = RequestSource("job")
	RequestSourceEvent  = RequestSource("event")
	RequestSourceSystem = RequestSource("system")
)

Variables

View Source
var (
	ErrIdempotencyIdLength  = errors.New("idempotency id: not 36 characters")
	ErrIdempotencyIdFormat  = errors.New("idempotency id: not a canonical hex UUID")
	ErrIdempotencyIdVersion = errors.New("idempotency id: not UUID version 7")
	ErrIdempotencyIdVariant = errors.New("idempotency id: not the RFC 9562 variant")
)
View Source
var CtxKeyDomainConstraints = contextKey{"domain_constraints"}

Functions

func BatchRowOf

func BatchRowOf(ctx context.Context) (int, bool)

BatchRowOf returns the row SetBatchRow recorded; false outside a batch.

func IdempotencyIdOf

func IdempotencyIdOf(ctx context.Context) (string, bool)

IdempotencyIdOf never generates one: an ID minted here would differ per call and hide a lost ID.

func NewIdempotencyId

func NewIdempotencyId() (string, error)

func ParseIdempotencyId

func ParseIdempotencyId(raw string) (string, error)

ParseIdempotencyId accepts only the canonical 8-4-4-4-12 form of a UUIDv7 and returns it lowercased. URN, braces, a list and the nil UUID all fail one of the checks below.

func ReplaceInner

func ReplaceInner(ctx Context, inner context.Context) bool

ReplaceInner swaps ctx's inner context in place and reports whether it could. It is for a layer that scopes a value to the call it wraps, such as the current span, on a context its callee mutates in place (acting org, request action): deriving a clone instead would hide those mutations from the caller. The owning-goroutine rule of the setters applies.

func RequestActionOf

func RequestActionOf(ctx context.Context) string

RequestActionOf returns the custom action code ctx was labelled with, or "".

func SetBatchRow

func SetBatchRow(ctx Context, row int)

SetBatchRow records that ctx is now writing the row-th (1-based) record of a batch, such as a bulk create or an import; 0 clears it. One request writing many records needs it to tell their side effects apart deterministically, e.g. the operation key of each record's sequence number, so that a retry with the same idempotency id addresses the same rows again. The owning-goroutine rule of the setters applies.

func SetRequestAction

func SetRequestAction(ctx Context, actionCode string)

SetRequestAction labels the writes ctx makes from now on with a custom action code, such as "suspend": the audit trail records an update made under it as that action. The request guard sets it when a custom permission check passes, so a custom action needs no audit code of its own. Stored in place, like SetRequestMeta.

func SetRequestMeta

func SetRequestMeta(ctx Context, meta RequestMeta)

SetRequestMeta stores meta on ctx in place. It lives in the inner context's values, so CloneRequestContext and every context derived from the inner one carry it along.

func WithIdempotencyId

func WithIdempotencyId(ctx context.Context, id string) context.Context

WithIdempotencyId derives a context carrying id. Set it on the inner context before a RequestContext wraps it, so CloneRequestContext and every derived context carry it too.

func WithRequestMeta

func WithRequestMeta(ctx context.Context, meta RequestMeta) context.Context

WithRequestMeta derives a plain context carrying meta, for an entry point that has no RequestContext yet: a corectx built on top of it sees the same meta.

Types

type Context

type Context interface {
	context.Context

	InnerContext() context.Context
	GetLogger() logging.LoggerService
	SetLogger(logger logging.LoggerService)
	GetDbTranx() db.DbTransaction
	SetDbTranx(trx db.DbTransaction)
	// OnCommitted runs fn after the context's transaction commits, or right away when there is
	// none, because the write it follows has already autocommitted. Use it for side effects
	// (event publishes, cache invalidation) that must not escape a rolled-back write.
	OnCommitted(fn func())
	GetDomainConstraints() dmodel.DynamicFields
	SetDomainConstraints(constraints dmodel.DynamicFields)
	GetModuleName() string
	GetPermissions() ContextPermissions
	SetPermissions(permissions ContextPermissions)
	GetUser() dmodel.DynamicFields
	SetUser(user dmodel.DynamicFields)
	// Replace current inner context with a new one that has the given key and value.
	WithValue(key, val any)
}

func AsRequestContext

func AsRequestContext(echoCtx *echo.Context) (Context, error)

Returns pointer to an instance of RequestContext if it exists, otherwise returns an error.

func CloneRequestContext

func CloneRequestContext(ctx Context) Context

CloneRequestContext returns a copy of ctx that can be given a different transaction without disturbing the original. Use it to derive a scoped context from a live request; the New* constructors above build an empty one, for code with no caller behind it.

It copies every field the copy might be used with, the caller's identity above all: basemodel stamps created_by, and the audit log its actor, from GetPermissions().UserId, so a clone that dropped permissions would write them null and report nothing. Domain constraints live in the inner context's values, so copying Context carries them along.

A field added to RequestContext must be added here too.

func CloneWithInner

func CloneWithInner(ctx Context, inner context.Context) Context

CloneWithInner is CloneRequestContext with the inner context replaced, for a caller that has derived a cancellation or deadline from the request's own context and needs the identity, logger and transaction to travel with it. Passing the bare inner context instead would drop all of those; mutating the original would impose the deadline on the caller's remaining work.

func NewRequestContext

func NewRequestContext(ctx context.Context) Context

func NewRequestContextF

func NewRequestContextF(ctx context.Context, moduleName string, domainConstraints dmodel.DynamicFields) Context

NewRequestContextF builds an empty context carrying domain constraints.

The constraints go through SetDomainConstraints rather than a struct field: GetDomainConstraints reads them back off the inner context's values, so a field set here would never be read.

func NewRequestContextM

func NewRequestContextM(ctx context.Context, moduleName string) Context

type ContextPermissions

type ContextPermissions struct {
	IsOwner      bool
	Entitlements ds.Set[string]
	UserId       model.Id `json:"user_id"`
	// The actor executing this request: a user, or a service acting for the system.
	Principal Principal `json:"principal"`
	// The orgs that user belongs to (if any)
	UserOrgIds ds.Set[model.Id] `json:"user_org_ids"`
	// The org unit that user belongs to (if any)
	OrgUnitId *model.Id `json:"org_unit_id"`
	// The org that the org unit belongs to (if user belongs to an org unit)
	OrgUnitOrgId *model.Id `json:"org_unit_org_id"`
}

type Principal

type Principal struct {
	Kind PrincipalKind `json:"kind"`
	Id   model.Id      `json:"id"`
	// OrgId is the single org a service principal acts within. A service holds no org
	// membership, so this is what org-scoped work is checked against; nil for a user, whose
	// reach comes from UserOrgIds instead.
	OrgId *model.Id `json:"org_id"`
	// DisplayName is for audit and logs only. Never make a security decision from it.
	DisplayName string `json:"display_name"`
}

Principal is the authenticated actor behind the current execution.

A zero Principal means "nobody was authenticated". Authorization must fail closed on it rather than reading absence as permission - see requestguard.AssertPermission.

func (Principal) IsZero

func (this Principal) IsZero() bool

IsZero reports whether no principal was established.

type PrincipalKind

type PrincipalKind string

PrincipalKind says what sort of actor is executing, so that a job or a consumer is distinguishable from an unauthenticated caller rather than looking identical to one.

This is not IAM's PrincipalType (`nikkiuser` | `custom`), which describes where a login credential came from - a different axis, and in a module core cannot import.

type RequestContext

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

RequestContext is owned by one goroutine, the one serving the request, which alone may call its setters. It is nevertheless read from others: database/sql watches Done/Err of every query's context from its own goroutine, and so does anything else handed the context to cancel on. Those readers only use the context.Context methods, so the inner context - the one field the setters replace and those methods read - is held atomically. Embedding it as a plain interface field let a reader see a torn value mid-WithValue, which crashed the process (core-001).

func (*RequestContext) Deadline

func (this *RequestContext) Deadline() (deadline time.Time, ok bool)

func (*RequestContext) Done

func (this *RequestContext) Done() <-chan struct{}

func (*RequestContext) Err

func (this *RequestContext) Err() error

func (*RequestContext) GetDbTranx

func (this *RequestContext) GetDbTranx() db.DbTransaction

func (*RequestContext) GetDomainConstraints

func (this *RequestContext) GetDomainConstraints() dmodel.DynamicFields

func (*RequestContext) GetLogger

func (this *RequestContext) GetLogger() logging.LoggerService

func (*RequestContext) GetModuleName

func (this *RequestContext) GetModuleName() string

func (*RequestContext) GetPermissions

func (this *RequestContext) GetPermissions() ContextPermissions

func (*RequestContext) GetUser

func (this *RequestContext) GetUser() dmodel.DynamicFields

func (*RequestContext) InnerContext

func (this *RequestContext) InnerContext() context.Context

func (*RequestContext) OnCommitted

func (this *RequestContext) OnCommitted(fn func())

func (*RequestContext) SetDbTranx

func (this *RequestContext) SetDbTranx(trx db.DbTransaction)

func (*RequestContext) SetDomainConstraints

func (this *RequestContext) SetDomainConstraints(constraints dmodel.DynamicFields)

func (*RequestContext) SetLogger

func (this *RequestContext) SetLogger(logger logging.LoggerService)

func (*RequestContext) SetPermissions

func (this *RequestContext) SetPermissions(permissions ContextPermissions)

func (*RequestContext) SetUser

func (this *RequestContext) SetUser(user dmodel.DynamicFields)

func (*RequestContext) Value

func (this *RequestContext) Value(key any) any

func (*RequestContext) WithValue

func (this *RequestContext) WithValue(key, val any)

Replace current inner context with a new one that has the given key and value.

The load and store are separate, which is safe only because the setters are called by the owning goroutine alone; concurrent readers always see either the old or the new inner context.

type RequestMeta

type RequestMeta struct {
	Source RequestSource
}

RequestMeta says what started the execution. Records one call writes are correlated by the active span's trace id instead (see auditlog), so the meta carries no id of its own.

func EnsureRequestMeta

func EnsureRequestMeta(ctx Context, fallbackSource RequestSource) RequestMeta

EnsureRequestMeta returns the meta ctx carries, or one with fallbackSource stored on ctx, so later writes through the same ctx share it. An entry point should set the meta itself: a meta stored here does not reach a context cloned from ctx before this call.

func NewRequestMeta

func NewRequestMeta(source RequestSource) RequestMeta

NewRequestMeta returns a meta for an execution started by source.

func RequestMetaOf

func RequestMetaOf(ctx context.Context) (RequestMeta, bool)

RequestMetaOf returns the meta ctx carries, and false when none was set.

type RequestSource

type RequestSource string

RequestSource says what started the execution: an HTTP request, a scheduled job, a message consumer, or the platform itself.

Jump to

Keyboard shortcuts

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