Chapter 65 — Project 2: distributed authorization with proven tenant isolation
Brief
Part 5 Ch 29 built end-to-end authorization for SecureShop as a standalone exercise. This project raises the bar in the direction that matters most for a real multi-tenant SaaS: an AUTOMATED, CONTINUOUS proof that tenant isolation holds — across every service, every data store, and every code change — rather than a one-time demonstration. You will build the "as tenant B" test harness as a genuine CI gate and then try, deliberately, to sneak a cross-tenant bug past it.
Tasks
1. BUILD THE FULL STACK (Part 5 Ch 23-28, reused, not repeated)
[ ] SPIFFE identities, token exchange, the OpenFGA/SpiceDB ReBAC
model, RLS-backed Postgres isolation, and the resilience
controls (rate limits, quotas, circuit breakers) exactly as
Part 5 specifies -- this is the STARTING POINT, not the
deliverable.
2. BUILD THE CONTINUOUS ISOLATION-PROOF HARNESS (the actual point of
this project)
[ ] an automated test suite that, for EVERY read/write endpoint
across EVERY service, authenticates as a user in TENANT A,
then attempts every plausible cross-tenant access: reading
tenant B's objects by ID, listing operations that should be
scoped, search queries, cache reads, exported reports, and
event-stream consumption -- and asserts ZERO tenant-B data is
ever returned or visible.
[ ] run this harness on EVERY PULL REQUEST (Part 9 Ch 58's SDLC
gate pattern, applied specifically to tenant isolation) -- not
as a manual, occasional exercise.
[ ] include the "new enemy" consistency check (Part 5 Ch 25):
revoke a sharing relationship and immediately assert the
revoked user can no longer see the resource, accounting for
the authorization engine's consistency guarantees.
3. TRY TO BREAK IT (this is required, not optional)
[ ] deliberately introduce THREE realistic cross-tenant bugs, one
at a time, each modelled on Part 5 Ch 27's "where the missing
filter hides" list (a new endpoint that forgot the ownership
check; a cache key missing the tenant prefix; an event consumer
that doesn't filter by tenant).
[ ] for each, confirm the CI harness catches it BEFORE merge. If
any bug slips through, that's a gap in the harness itself --
fix the harness, not just the bug, and re-introduce the same
bug class to confirm the harness now catches it.
4. EXTEND TO A DATA STORE PART 5 DIDN'T COVER IN DEPTH
[ ] add the harness's coverage to at least one of: the object-
storage layer (Part 7 Ch 42's prefix/key-based isolation), the
Kafka event stream (Part 5 Ch 26's per-tenant topic/ACL model),
or a cache layer (Part 5 Ch 27) -- proving the SAME automated-
proof discipline extends beyond the primary database.
Deliverable
~/sec-lab/adv/projects/p2-authz/report.md: the stack architecture (reused
from Part 5), the isolation-proof harness's design and full endpoint coverage
list, CI configuration showing it runs on every PR, the three deliberately-
introduced bugs with the harness's catch (or initial miss-and-fix) evidence,
and the extended-coverage data store's proof.
Definition of done
- An automated isolation-proof harness covering every endpoint across every service, running on every PR.
- The "new enemy" consistency check implemented and passing.
- All three deliberately-introduced cross-tenant bugs caught by the harness before merge (with evidence of the harness being fixed if any initially slipped through).
- Coverage extended to at least one non-primary-database data store.
Rubric (/100)
Stack correctly reused from Part 5 ..................... 10
Isolation-proof harness: coverage + CI integration ...... 30
"New enemy" consistency check ........................... 10
Three deliberate bugs, all caught (or harness fixed) .... 30
Extended coverage to a second data store ................ 15
Report .................................................... 5
Further reading
- Revisit Part 5 Ch 25/27 in full — this project is specifically about turning their manual worked examples into a continuously-running, self-testing guarantee.
- Pattern: "continuous authorization testing" / "policy testing in CI" write-ups from OpenFGA's and SpiceDB's own documentation — both platforms publish guidance on exactly this kind of automated proof.