Files
upstream-ory-keto/internal/check/definitions.go
upstream-mirror 9456a4e922
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:08:34 +05:00

142 lines
4.3 KiB
Go

// Copyright © 2026 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package check
import (
"context"
"github.com/ory/keto/internal/relationtuple"
)
type (
// Result holds the outcome of a single permission check.
// Limitation is non-empty only when Membership is MembershipUnknown.
Result struct {
Membership Membership
Err error
Limitation LimitationKind
}
Membership int
// LimitationKind identifies which traversal constraint was hit. A non-empty
// value on a Result means the membership answer is indeterminate.
LimitationKind string
)
//go:generate stringer -type Membership
const (
MembershipUnknown Membership = iota
IsMember
NotMember
)
const (
LimitationMaxDepthExceeded LimitationKind = "max_depth_exceeded"
LimitationMaxWidthExceeded LimitationKind = "max_width_exceeded"
)
var (
ResultUnknown = Result{Membership: MembershipUnknown}
ResultIsMember = Result{Membership: IsMember}
ResultNotMember = Result{Membership: NotMember}
)
// CheckRequest carries all inputs for a single check evaluation step.
type CheckRequest struct {
Tuple *relationtuple.RelationTuple
RestDepth int
}
func (r CheckRequest) WithDepth(d int) CheckRequest {
r.RestDepth = d
return r
}
// PlannedStep pairs a Step with the CheckRequest it should run against.
type PlannedStep struct {
Step Step
Req CheckRequest
}
// StepRunner orchestrates execution of multiple steps with short-circuit semantics.
// Typical usage:
//
// runner := ex.NewUnionRunner(ctx)
// for _, ps := range steps {
// if runner.Done() { break }
// runner.Add(ps)
// }
// return runner.Result()
type StepRunner interface {
// Add enqueues a step for execution. Returns early if Done() is true.
// No-op if the runner has already reached a decisive result or been cancelled.
Add(PlannedStep)
// Done reports whether a decisive result was reached or context cancelled.
// Use to short-circuit the enqueue loop.
Done() bool
// Ctx returns the runner's context, usable for shared state like visited tracking.
// The context is cancelled when a decisive result is reached (to signal siblings).
Ctx() context.Context
// AddLimitation records that some branches were skipped due to given [LimitationKind] constraint.
AddLimitation(LimitationKind)
// Result waits for all in-flight steps to complete and returns the aggregated result.
// Call it after the Add loop finishes (or breaks early on Done()).
Result() Result
}
// Executor coordinates permission check execution with middleware support.
// It runs individual steps (Run), sequences steps with union/intersection logic
// (RunUnion, RunIntersection), and supports custom execution strategies via
// middleware. All Executor implementations are stateless; dependencies come
// from EngineDependencies.
//
// Typical usage:
//
// result := executor.Run(ctx, DirectStep{}, req)
// unionResult := executor.RunUnion(ctx, step1, step2)
// intersectionResult := executor.RunIntersection(ctx, step1, step2)
type Executor interface {
// Run executes a single step through the middleware chain.
Run(ctx context.Context, step Step, req CheckRequest) Result
// RunUnion runs steps with OR semantics: returns IsMember on first match,
// short-circuits siblings, falls through to NotMember if none match.
RunUnion(ctx context.Context, steps ...PlannedStep) Result
// RunIntersection runs steps with AND semantics: returns NotMember or Error
// on first failure, short-circuits siblings.
RunIntersection(ctx context.Context, steps ...PlannedStep) Result
// NewUnionRunner creates a StepRunner for streaming union execution.
// It is useful when Steps are created dynamically and the full list is not known upfront.
NewUnionRunner(ctx context.Context) StepRunner
Deps() EngineDependencies
}
// Middleware intercepts a step execution within the pipeline. next continues
// down the remaining middlewares and ultimately calls step.Execute.
type Middleware func(
ctx context.Context,
step Step,
req CheckRequest,
next func(context.Context, Step, CheckRequest) Result,
) Result
// Checker performs a full permission check for a single relation tuple.
// Implemented by Executor in the step package.
type (
CheckerProvider interface {
Checker() Checker
}
Checker interface {
CheckRelationTuple(ctx context.Context, r *relationtuple.RelationTuple, restDepth int) Result
}
)