cedar

package
v0.11.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package cedar is the cedar-go authorization driver (ADR-0074) behind the auth.Authorizer port (ADR-0018): it answers per-object, fine-grained data-plane decisions (KV first) that the coarse RBAC driver cannot express. It is DEFAULT-DENY — with no permitting Policy a kv::read is denied; the built-in forbid(kv::write) unless principal == resource.owner makes writes single-writer. The compiled PolicySet is cached and recompiled on a Policy change; per call only the request-relevant entities (principal Function + resource KVTable + its parent KVStore) are resolved from the metastore, never a full-store rebuild. It is a leaf under internal/auth, like the rbac driver.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompileEgressPolicy

func CompileEgressPolicy(ns v1.NamespaceName, ep *v1.EgressPolicy, funcs []v1.ObjectName) ([]v1.Policy, error)

CompileEgressPolicy compiles one namespace's EgressPolicy into SYNTHETIC v1.Policy objects (Spec.Cedar), listed by the policy source alongside user KindPolicy and compiled by the existing compile()/ NewPolicyListFromBytes path — NOT a pre-built PolicySet (ADR-0117, M1). ONE permit per RULE (the namespace-attribute collapse): a domain/wildcard matches via resource.domains.contains, a CIDR via resource.ip.isInRange(ip("…")), ports as a set. The principal scope is the entity's existing `namespace` attribute (`principal is Function && principal.namespace == "<ns>"`) for a whole-namespace policy, or a bounded disjunction over the explicit spec.appliesTo list (`principal == Function::"<ns>/<fn>"`) — O(rules), independent of the namespace Function count, using NO Cedar group entity and NO edit to the registry assembly. funcs is the namespace Function-set (drives the cache revision; the compiled set does not depend on it). A nil ep or no rules ⇒ no policies (default-deny holds).

Each permit is assembled as a Cedar JSON EST document and parsed with cedar.Policy.UnmarshalJSON (injection-safe: every domain/CIDR is a JSON value node, never interpolated syntax; a malformed CIDR is a compile error, never templated). MarshalCedar renders each permit back to canonical Cedar text to preserve the M1 PolicySource→compile() contract.

func CompileRolesAssignment

func CompileRolesAssignment(ctx context.Context, ra *v1.RolesAssignment, roles RoleResolver) ([]v1.Policy, error)

CompileRolesAssignment compiles a RolesAssignment's READ/query/invoke grants into synthetic v1.Policy Cedar permits (ADR-0136; the injection-safe EST path, mirroring CompileEgressPolicy). WRITE grants are NOT permits (a Cedar forbid overrides a permit) — they are surfaced via WriterGrants for the resource materializer's `writers` set. A missing role / empty grant ⇒ no policy (default-deny holds).

func FunctionGrants added in v0.5.0

func FunctionGrants(ns v1.NamespaceName, name v1.ObjectName, text string) (map[v1.ObjectName][]string, error)

FunctionGrants lists, per Function of namespace ns that a statement of Policy name references anywhere (its scope or its conditions), one grant per such statement: "policy/<ns>/<name>#<j>@<sha256 of the statement>", where "<ns>/<name>#<j>" is the ID the PDP compiles that statement under. A grant names what it grants, never its principal, so Functions referenced by one statement share it.

func KnownAction

func KnownAction(action string) bool

KnownAction reports whether action is in the assembled schema vocabulary (ADR-0116).

func KnownEntityType

func KnownEntityType(entityType string) bool

KnownEntityType reports whether entityType is in the assembled schema vocabulary (ADR-0116).

func New

func New(d Deps) (auth.Authorizer, error)

New builds the cedar driver behind the auth.Authorizer port (ADR-0074). Entities + Policies are required.

func ValidateCedar

func ValidateCedar(text string) error

ValidateCedar checks that text PARSES as Cedar and that every statement references only the curated actions + entity types (ADR-0074 policy-validity). It returns fault.Invalid on a parse error or an off-schema action/entity-type — the policy-validity admission surfaces it. cedar-go's own schema validator is experimental, so this is the curated, fixed-schema check the driver owns.

func ValidateCedarInNamespace added in v0.5.0

func ValidateCedarInNamespace(ns v1.NamespaceName, text string) error

ValidateCedarInNamespace runs ValidateCedar, then refuses a statement whose principal or resource scope (`==`, `in`, `is … in`) names a namespaced entity outside ns (ADR-0177 Decision 3). Unconstrained scopes and entities inside `when`/`unless` pass: the per-namespace PolicySet contains them.

Types

type Capability

type Capability struct {
	// Name is the capability's short id (e.g. "kv", "invoke", "s3", "egress") — for diagnostics/dedup.
	// Unique across the registry.
	Name string
	// Actions are the Cedar actions this capability authorizes (e.g. auth.ActionKVRead, ActionKVWrite).
	// Unique across the registry (no two capabilities share an action).
	Actions []auth.Action
	// EmitsEntityType is the primary Cedar entity type this capability materializes as a resource (e.g.
	// entityTypeKVTable). Used ONLY for schema-vocabulary assembly (KnownEntityType) — NOT a dispatch key
	// (an EntityRef.Type v1.Kind never equals the Cedar entity type: KindKVStore→KVTable, KindBucket→
	// BlobPrefix). Resource dispatch is by the Resource func's ok, below.
	EmitsEntityType cedartypes.EntityType
	// SupportingEntityTypes are the additional Cedar entity types this capability contributes to the
	// schema vocabulary beyond its primary EmitsEntityType — the resource's parent (KVStore for kv,
	// Bucket for s3) and any alternate principal type it admits (S3Identity for s3). A Policy may scope
	// on these (`resource in KVStore::"…"`, `principal == S3Identity::"…"`), so KnownEntityType must
	// accept them; they are assembled from the registry exactly like EmitsEntityType (never hand-listed).
	SupportingEntityTypes []cedartypes.EntityType
	// Resource materializes the resource entity (UID + parents + owner attr) for a request whose resource
	// this capability owns. ok==false ⇒ not ours (try the next capability) — this ok is the SOLE resource-
	// dispatch mechanism. A dangling resource still returns ok==true with bare entities (evaluable,
	// default-deny). nil ⇒ the capability owns no resource shape (Policy-only over an existing entity).
	Resource func(ctx context.Context, r MetaReader, resource auth.EntityRef) (em cedartypes.EntityMap, ok bool, err error)
	// PrincipalBinding declares this capability's binding-as-grant attribute on the principal: Attr is the
	// principal attribute name (e.g. "kvBindings"); Bind materializes its members from a resolved
	// principal object (see PrincipalSource). The provider sets Attr whenever Bind returns a non-nil slice
	// (possibly empty), matching today's EntitiesFor (a Function always carries links/kvBindings/blobBindings;
	// the built-ins also `has`-guard, so empty ≡ absent). A Bind that returns nil for a source type
	// contributes nothing (attr absent) — e.g. kv/invoke for a CatalogService. Zero value ⇒ no binding.
	PrincipalBinding PrincipalBinding
	// Builtin is this capability's built-in permit policy text (the .cedar granting the capability when
	// principal.<attr>.contains(resource)); "" ⇒ no built-in (Policy-only). Each policy carries a unique
	// @id annotation, its stable name in a deny reason.
	Builtin string
}

Capability declares one authorization capability (kv, invoke, s3, egress, …) so the schema vocabulary, the EntityProvider, and the built-in permits are ASSEMBLED from the registered set — registering a new capability adds no branch to shared code (ADR-0116). All funcs are typed (no any).

Two orthogonal axes are kept separate (judge B1): a Capability is an {action + resource + built-in} on a PRINCIPAL; *where a principal's binding attributes come from* is a PrincipalSource (below), so the ADR-0088 provider principal (a CatalogService, ADR-0175) is NOT a fourth capability — s3 is one capability with two principal sources.

func CatalogCapability

func CatalogCapability() Capability

CatalogCapability is the catalog::query capability (ADR-0137): spec.catalogs → the `catalogBindings` Set of CatalogService entity-refs (binding-as-query-grant); the resource is the CatalogService the query targets. It is READ-SHAPED — like invoke it owns no writable resource, so there is NO WriterLister and NO write action; the built-in permit reads principal.catalogBindings, not the resource's attrs, so default-deny holds even if the target is missing. External Identities are granted via a Catalog-scoped RolesAssignment (compiled to a permit), not this built-in.

func EgressCapability

func EgressCapability() Capability

EgressCapability registers egress on the ADR-0116 registry (ADR-0117, F81): Action egress::connect; resource entity NetDestination materialized from the request EntityRef's Path (no MetaReader read — the destination is ephemeral); NO PrincipalBinding (governed by EgressPolicy, not a spec.<field> grant) and NO Builtin (default-deny — grants come only from a compiled EgressPolicy). Its Resource emits the UID + attributes {ip, port, domains}; the driver's resourceUID gains a matching KindNetDestination case (entities.go).

func InvokeCapability

func InvokeCapability() Capability

InvokeCapability is the link::invoke capability (ADR-0075): spec.links → the `links` Set of target Function entities (link-as-grant); the resource is the target Function (a bare entity — the built-in permit reads principal.links, not the target's attrs, so default-deny holds even if the target is missing). It owns no principal-owned resource beyond the target Function.

func KVCapability

func KVCapability() Capability

KVCapability is the kv::read/kv::write capability (ADR-0074/0076): spec.kv → the `kvBindings` Set of KVTable entity-refs (read binding-as-grant); the resource is the KVTable + its parent KVStore + the table owner (a Function entity-REFERENCE, so `principal == resource.owner` compares entities). It owns both built-ins: the write single-writer rule and the read binding-grant.

func KVCapabilityWithWriters

func KVCapabilityWithWriters(w WriterLister) Capability

KVCapabilityWithWriters is KVCapability with a WriterLister so a writer-role RolesAssignment (ADR-0136) contributes to a table's `writers` set. KVCapability() (nil lister) is owner-only (back-compat).

func S3Capability

func S3Capability() Capability

S3Capability is the s3::read/s3::write capability (ADR-0080): spec.blob → the `blobBindings` Set of BlobPrefix entity-refs (read binding-as-grant); the resource is the BlobPrefix + its parent Bucket + the prefix owner (a Function entity-REFERENCE). Its Bind handles BOTH a *v1.Function and a *v1.CatalogService (ADR-0088 — both carry spec.blob). It admits the external S3Identity principal type.

func S3CapabilityWithWriters

func S3CapabilityWithWriters(w WriterLister) Capability

S3CapabilityWithWriters is S3Capability with a WriterLister so a writer-role RolesAssignment (ADR-0136) contributes to a prefix's `writers` set (the generalized single-writer forbid). The compose root uses this; S3Capability() (nil lister) is owner-only (back-compat).

type Deps

type Deps struct {
	// Entities resolves the request-relevant Cedar entities per call (principal + resource + parents).
	Entities EntityProvider
	// Policies supplies the user Policy resources (compiled + cached with the built-ins).
	Policies PolicySource
	// Builtins is the always-on built-in Cedar policy text (a Registry's Builtins()) compiled into the
	// PolicySet with the user Policies. Empty ⇒ the default registry's built-ins — so the compose root can
	// supply a variant built-in set (e.g. funcdctl dev's relaxed S3 writes) without a package global.
	Builtins string
	// Logger is the driver's logger; nil ⇒ slog.Default().
	Logger *slog.Logger
}

Deps configures the cedar driver (ADR-0074).

type EntityProvider

type EntityProvider interface {
	EntitiesFor(ctx context.Context, principal, resource auth.EntityRef) (cedartypes.EntityMap, error)
}

EntityProvider resolves ONLY the request-relevant Cedar entities (the principal + the resource + its parents) for one Authorize call (ADR-0074) — never a full-store rebuild. Entities are materialized from the metastore's existing resources (no duplicate state); the registry (ADR-0116) composes it from the registered capabilities' resource materializers + principal-binding Sets.

func NewEntityProvider

func NewEntityProvider(r MetaReader) (EntityProvider, error)

NewEntityProvider builds the default metastore-backed EntityProvider (ADR-0074): the composite provider assembled from the default capability registry (kv, invoke, s3 + the Function/CatalogService sources). r is required. Consumers that register additional capabilities build their own Registry and call (*Registry).EntityProvider directly (ADR-0116).

type MetaReader

type MetaReader interface {
	Get(ctx context.Context, gvk v1.GroupVersionKind, ns v1.NamespaceName, name v1.ObjectName) (v1.Object, error)
}

MetaReader is the read-only metastore view the EntityProvider needs (ADR-0074): it reads the caller Function (for the principal entity) and the target resource (for the owner/parent). Declared here so the cedar package stays a near-leaf; the wiring adapts store.Store.

type PolicySource

type PolicySource interface {
	// Policies returns all Policy resources and an opaque revision; the driver recompiles only when
	// the revision changes (cheap cache invalidation without diffing policy text).
	Policies(ctx context.Context) (policies []v1.Policy, revision string, err error)
}

PolicySource supplies the user Policy resources the driver compiles (ADR-0074). The driver compiles the built-in rules + each namespace's user Policies into one cached PolicySet per namespace (ADR-0177), recompiled when the source's revision changes.

type PrincipalBinding

type PrincipalBinding struct {
	// Attr is the Cedar principal attribute (e.g. "kvBindings", "links", "blobBindings").
	Attr string
	// Bind materializes the attribute's Set members from a resolved principal object. src is whatever a
	// PrincipalSource resolved (a *v1.Function or a *v1.CatalogService); a capability that doesn't apply to
	// that source type returns nil (e.g. kv/invoke return nil for a CatalogService — providers have no
	// links/kv). ns is the principal namespace (for building same-namespace resource UIDs).
	Bind func(ns v1.NamespaceName, src PrincipalObject) []cedartypes.EntityUID
}

PrincipalBinding is one binding-as-grant attribute a capability contributes to a principal.

type PrincipalObject

type PrincipalObject = v1.PrincipalObject

PrincipalObject is a resolved principal-backing object (a *v1.Function or *v1.CatalogService); a closed interface (only api/types implements it, via an unexported marker) so PrincipalBinding.Bind type-switches without any. It aliases v1.PrincipalObject: the marker method must live in the same package as the implementing types, so the sealed interface is declared in api/types/v1alpha1 and re-exported here under the cedar name.

type PrincipalSource

type PrincipalSource func(ctx context.Context, r MetaReader, p auth.EntityRef) (obj PrincipalObject, ok bool, err error)

PrincipalSource resolves the principal's backing object for a principal EntityRef, in order. The default sources are Function and CatalogService (ADR-0088), each resolving only a principal of its own type (ADR-0175) — the provider case is a principal SOURCE, not a capability. A source returns ok==false to defer to the next.

func CatalogServicePrincipalSource

func CatalogServicePrincipalSource() PrincipalSource

CatalogServicePrincipalSource resolves a CatalogService principal's backing *v1.CatalogService (ADR-0088, ADR-0175): an engine is its own principal, never a same-named Function. Its spec.blob are its blobBindings; it has no links/kv.

func FunctionPrincipalSource

func FunctionPrincipalSource() PrincipalSource

FunctionPrincipalSource resolves a Function principal's backing *v1.Function (ADR-0074); a principal of any other type is not its own (ADR-0175).

type Registry

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

Registry is a deduped set of capabilities + an ordered principal-source list. It assembles the shared cedar artifacts (the composite EntityProvider, the KnownAction/KnownEntityType vocabulary, and the built-in PolicySet) from the registered set (ADR-0116).

func NewRegistry

func NewRegistry(caps []Capability, sources []PrincipalSource) (*Registry, error)

NewRegistry validates (unique Name, unique Action across capabilities; ≥1 source) and returns the registry. A duplicate Name/Action or an empty source list is a fault.Invalid wiring error.

func (*Registry) Builtins

func (reg *Registry) Builtins() string

Builtins returns the concatenated built-in permit policy text (the union of the capabilities' Builtin, in registry order so the compiled PolicySet is deterministic).

func (*Registry) EntityProvider

func (reg *Registry) EntityProvider(r MetaReader) (EntityProvider, error)

EntityProvider returns the composite provider over r (ADR-0116). r is required.

func (*Registry) KnownAction

func (reg *Registry) KnownAction(a auth.Action) bool

KnownAction reports whether a is one of the registered capabilities' actions (the assembled schema vocabulary — replaces the hand-listed curatedActions map).

func (*Registry) KnownEntityType

func (reg *Registry) KnownEntityType(t string) bool

KnownEntityType reports whether t is one of the registered capabilities' entity types (primary EmitsEntityType ∪ SupportingEntityTypes — the assembled schema vocabulary, replacing the hand-listed curatedEntityTypes map).

type RoleResolver

type RoleResolver interface {
	Actions(ctx context.Context, ref v1.RoleRef, ns v1.NamespaceName) ([]string, bool)
}

RoleResolver resolves a RoleRef (a built-in name or a custom Role resource) to its action set.

func NewRoleResolver

func NewRoleResolver(r MetaReader) RoleResolver

NewRoleResolver builds the production role resolver over a MetaReader.

type WriterGrant

type WriterGrant struct {
	Principal cedartypes.EntityUID
	Scope     v1.ScopeRef
}

WriterGrant is one principal authorized to WRITE a scope (a s3::write / kv::write grant).

func WriterGrants

func WriterGrants(ctx context.Context, ra *v1.RolesAssignment, roles RoleResolver) []WriterGrant

WriterGrants returns the write-authorizing (principal, scope) grants of a RolesAssignment — consumed by the s3/kv resource materializer to build a resource's `writers` set (the generalized single-writer forbid). Excludes the legacy owner (added separately by the materializer).

type WriterLister

type WriterLister interface {
	BlobWriters(ctx context.Context, ns v1.NamespaceName, bucket, prefix string) []cedartypes.EntityUID
	KVWriters(ctx context.Context, ns v1.NamespaceName, store, table string) []cedartypes.EntityUID
}

WriterLister returns the writer principal UIDs (beyond the legacy owner) authorized to WRITE a blob prefix / kv table, from writer-role RolesAssignments (ADR-0136). It backs the generalized single-writer forbid's `writers` set. A nil WriterLister ⇒ owner-only writers (back-compat, no role grants).

Jump to

Keyboard shortcuts

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