Skip to content

v1.6.0 — Conditional Permission Rich Signal

Choose a tag to compare

@danhtran94 danhtran94 released this 09 May 08:56
· 27 commits to main since this release

Surfaces SpiceDB's CONDITIONAL_PERMISSION signal as a typed error path. Recoverable failures (caller forgot to supply caveat context) are now distinguishable from hard denies via errors.Is(err, ErrConditionalPermission) and errors.As(err, &cpe) — cpe.MissingKeys carries the caveat parameter names from PartialCaveatInfo.MissingRequiredContext so callers can fetch and retry.

Documented as deferred work in CHANGELOG entries from v1.1.0 through v1.4.0; SPEC-007 closes the gap with zero codegen template change.

Added

  • `authz.ErrConditionalPermission` — sentinel for the rich-signal path.
  • `authz.ConditionalPermissionError` — typed struct with `MissingKeys []string`. Custom `Is` method matches BOTH `ErrConditionalPermission` AND `ErrPermissionDenied` (backward compat).
  • 4 new e2e tests covering granted, conditional-with-MissingKeys, backward-compat `ErrPermissionDenied` match, hard-deny-not-conditional.

Changed

  • *`spicedb.Engine.errorIfDenied` — switch on Permissionship; CONDITIONAL_PERMISSION → typed pointer error from `PartialCaveatInfo.MissingRequiredContext`. Single point of error construction propagates the rich signal to every Check method automatically.
  • Generated `Check` method bodies unchanged. `(bool, error)` return shape preserved. Zero `.gen.go` diff vs v1.5.0.

Caller migration (rich-signal opt-in)

```go
err := folder.CheckTenantedBrowse(ctx, input)
switch {
case err == nil:
// granted
case errors.Is(err, authz.ErrConditionalPermission):
var cpe *authz.ConditionalPermissionError
errors.As(err, &cpe)
// cpe.MissingKeys lists caveat keys to fetch and retry with
case errors.Is(err, authz.ErrPermissionDenied):
// hard deny — user genuinely lacks permission
}
```

Existing v1.5 callers checking only `errors.Is(err, ErrPermissionDenied)` see no behavior change.

Verified

  • All 4 e2e packages pass.
  • Codegen idempotent at the new baseline (zero diff vs v1.5.0).

Deferred

  • Lookup paths surfacing CONDITIONAL — `LookupResources` / `LookupSubjects` / `*WithCaveat` continue to silently filter `Permissionship != HAS_PERMISSION`. Returning a conditional-but-recoverable subset alongside definite IDs requires a typed-return shape change; deferred until concrete demand.
  • Auto-retry helper — surfacing missing keys is the engine's job; deciding whether to fetch and retry is the caller's. Future helper out of scope here.