perf(authz): round 8 — cache the File/Folder grant-cascade decision for shared-album thumbnails

get_thumbnail_impl runs require_permission(Read) on every request. For a drive
member that's a drive_role_cache hit, but a shared-album recipient — granted a
folder (the album), not drive membership — fails the drive-role precheck and
falls through to file_cascade_grant_exists (a role_grants ⋈ folders lpath
ancestor query), once per file. Browsers revalidate immutable thumbnails
constantly, so the same (recipient, file, Read) decision was recomputed on
every thumbnail of every view — ~100 grant queries per 100-photo album per
navigate-away-and-back.

New cascade_grant_cache ((Subject, Resource, Permission) → bool, 30 s TTL)
memoises that decision. The check is NEVER skipped — the ordering is unchanged,
authz still runs on every request; only the result is cached, and only after
the drive-role precheck fails (so a later drive grant can't be shadowed by a
stale entry). Invalidation mirrors drive_role_cache's convention: explicit
invalidate_all on every File/Folder set_role/clear_role (immediate revoke on
the direct share path), 30 s TTL for the indirect paths (group membership,
moves, expiry) "rather than a deep invalidation tree".

Bench (bench_thumbnail_cascade_cache) with hard safety gates — recipient
allowed, outsider denied, and a clear_role revoke denies the very next check
(proving the grant-write flush): 100-photo album revalidation 2576 → 2.70
µs/thumb (~950x), 257.6 → 0.27 ms/view. Validated against the full
--cfg integration_tests authz suite (554 tests) + 524 workspace tests, clippy
-D warnings clean.

Deliberately not done: moving authz after the 304/cache short-circuit (a
security-posture change — a revoked user could serve cached thumbnails). With
the decision cached, the authz on the 304 path is now a memory hit, so the
"zero DB work on a 304" intent is restored without weakening the check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aJu9ghvuT8WqC31ZEGTBA
This commit is contained in:
Claude
2026-07-18 14:03:43 +00:00
parent 7626dc95c1
commit 79b94126be
4 changed files with 601 additions and 26 deletions
+10
View File
@@ -350,6 +350,16 @@ name = "bench_micro_allocs"
path = "examples/bench_micro_allocs.rs"
required-features = ["bench"]
# Round-8 battery ─────────────────────────────────────────────────────────────
# Shared-album thumbnail authz — folder-grant cascade query per thumbnail vs
# the cascade_grant_cache; includes a revocation safety gate (needs the dev
# Postgres up).
[[example]]
name = "bench_thumbnail_cascade_cache"
path = "examples/bench_thumbnail_cascade_cache.rs"
required-features = ["bench"]
# Round-7 battery ─────────────────────────────────────────────────────────────
# Range-seek per-request authz duplication — the per-seek require the range