Documentation
¶
Overview ¶
Package authz answers "can this actor perform this action on this resource?" — a deliberate seam so the v1 always-allow implementation can later be swapped for Casbin / OpenFGA / Keto without touching call sites.
Handlers call:
if err := d.Authz.Authorize(ctx, "policy.write", authz.Resource{...}); err != nil {
return nil, err
}
before mutating state. Today the call is a near-no-op for any authenticated caller; tomorrow it gates the action against per-org permissions, ABAC predicates, or whatever the SaaS tier needs.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrForbidden = errors.New("authz: forbidden")
ErrForbidden is returned when an authenticated actor lacks permission. Mapped to HTTP 403.
var ErrUnauthenticated = errors.New("authz: unauthenticated")
ErrUnauthenticated is returned when the call has no actor in context. Mapped to HTTP 401 by the control-plane middleware.
Functions ¶
This section is empty.
Types ¶
type AlwaysAllowAuthenticated ¶
type AlwaysAllowAuthenticated struct{}
AlwaysAllowAuthenticated is the v1 implementation: any authenticated caller (user session or admin-token bypass) is granted any action. Unauthenticated callers are rejected.
This exists so handler call sites are permanent. Swap the concrete type when multi-tenant work lands; handlers stay the same.
type Authorizer ¶
type Authorizer interface {
Authorize(ctx context.Context, action string, resource Resource) error
}
Authorizer is the policy-decision interface. Authorize returns nil to allow, ErrForbidden/ErrUnauthenticated to deny, or any other error to signal a backend failure (which the middleware should treat as 500, not 403 — failure to authorize is not the same as denial).
type OwnerScoped ¶ added in v0.8.0
type OwnerScoped struct{}
OwnerScoped grants each authenticated user access to catalog reads and to rows they own, and reserves everything else for admins (the "admin" role or the RELAY_ADMIN_TOKEN break-glass).
The policy, per row owner:
- system / provider / host (catalog rows): readable by everyone, mutable by admins only (still behind settings.Governs).
- user rows with owner.ID == caller: fully owned.
- user rows with empty owner.ID (operator/grandfathered rows, incl. everything the admin token creates): admins only.
Mutations fail closed: an Authorize call that carries no Resource.Owner is treated as "not provably owned" and denied for non-admins. Handlers that operate on a specific row must fetch it and pass its owner.
type Resource ¶
type Resource struct {
Kind string // "provider", "policy", "model", ...
ID string // resource id, if known (UUID)
Name string // resource slug, if id is not known
// Owner is the target row's provenance, populated by handlers that
// have already fetched the row (mutations authorize twice as cheaply
// as fetching twice). Nil means "owner unknown" — owner-aware
// implementations must fail closed on mutations, not open.
Owner *meta.Owner
}
Resource describes the target of an authorization check. Fields are optional; populate what the action needs.
type Scoper ¶ added in v0.8.0
type Scoper interface {
// Visible reports whether the current actor may see a row of kind
// with the given owner in lists and reads.
Visible(ctx context.Context, kind string, owner meta.Owner) bool
}
Scoper is an optional interface an Authorizer may implement to gate per-row visibility in list/read paths, where per-row Authorize calls would be the wrong shape. Handlers filter rows through Visible when the configured Authorizer implements it; an Authorizer that doesn't (the default AlwaysAllowAuthenticated) leaves reads unfiltered.