diff --git a/articles/assets/authz-playground/day.png b/articles/assets/authz-playground/day.png new file mode 100644 index 0000000..99edb06 Binary files /dev/null and b/articles/assets/authz-playground/day.png differ diff --git a/articles/assets/authz-playground/graphs.png b/articles/assets/authz-playground/graphs.png new file mode 100644 index 0000000..675851a Binary files /dev/null and b/articles/assets/authz-playground/graphs.png differ diff --git a/articles/assets/authz-playground/map.png b/articles/assets/authz-playground/map.png deleted file mode 100644 index 7528144..0000000 Binary files a/articles/assets/authz-playground/map.png and /dev/null differ diff --git a/articles/assets/authz-playground/proj.png b/articles/assets/authz-playground/proj.png deleted file mode 100644 index f32b9dc..0000000 Binary files a/articles/assets/authz-playground/proj.png and /dev/null differ diff --git a/articles/authz-playground.md b/articles/authz-playground.md index 20e7fe7..206aa2b 100644 --- a/articles/authz-playground.md +++ b/articles/authz-playground.md @@ -20,9 +20,9 @@ So I gave four of them the same rules and ran every possible request through all - **Live demo:** [https://0-draft.github.io/authz-playground/](https://0-draft.github.io/authz-playground/) - **Repo:** [0-draft/authz-playground](https://github.com/0-draft/authz-playground) -![The divergence map](https://raw.githubusercontent.com/0-draft/dev.to/refs/heads/main/articles/assets/authz-playground/map.png?v=0d6c7044) +![One question, every engine, every hour of the day](./assets/authz-playground/day.png?v=6f3b819c) -One square is one request, one row per engine, filled means allowed. Pink is where they disagree. +One question, asked of every engine at every hour of the day. A raised line is allow. Red is where they disagree. ## Where ReBAC stops @@ -32,15 +32,22 @@ That third one is where ReBAC stops keeping up. `check(user, relation, object)` That gap is why OpenFGA later added Conditions. Their announcement names time of day as the example relationships handle badly. -The same rules side by side are worth as much as the decisions: +The playground now has that fix as a fifth row: the same Zanzibar model with an OpenFGA condition. The grant stays relational, and one more relation gates it: -![The same requirements in four languages](https://raw.githubusercontent.com/0-draft/dev.to/refs/heads/main/articles/assets/authz-playground/proj.png?v=7c8e686d) +```text +define within_hours: [user:* with business_hours] +define editor: (owner or admin from parent) and within_hours +``` + +It agrees with the other three on every request. It is still not free. A condition can only hang off a tuple, and business hours are about the request rather than any relationship, so every document needs a placeholder tuple whose only job is to carry the clock. The page rates it possible but forced, not native. + +![The classic model and the conditional one, asked the same question at 08:00](./assets/authz-playground/graphs.png?v=39f61897) Add a document and Casbin needs its policy table regenerated, while ReBAC just gains a tuple and the model never moves. ## What broke -The projections are where I could lie without noticing, so they get checked twice: all four engines run every request and any disagreement is reported, and a separate implementation written from the requirement text alone checks they are not all wrong together. +The projections are where I could lie without noticing, so they get checked twice: every engine runs every request and any disagreement is reported, and a separate implementation written from the requirement text alone checks they are not all wrong together. Both earned their keep. @@ -48,10 +55,12 @@ I gave the ReBAC model `viewer: owner or editor`, because obviously an editor ca Worse, the request enumeration sampled 03:00, 10:00, 14:00 and 22:00, so neither edge of the business-hours window was ever evaluated. I moved one engine's window to 08:00-19:00 to see what would happen. All fourteen tests passed. The suite that existed to protect the comparison could not see the one boundary the whole thing rests on. +The fix was to sample both edges of the window and the hour either side of them, 08, 09, 17 and 18, plus a test that fails if any of those ever drops out of the enumeration. The 08:00-19:00 window now fails at once. + ## How it runs No backend. Cedar through its official wasm bindings, Casbin's TypeScript build, and about a hundred lines reimplementing the Zanzibar check algorithm. Rego is OPA itself compiled to `js/wasm`, so policies are compiled and evaluated live: 8 MB gzipped, loaded lazily, roughly 2 ms per evaluation after the first. If you try that last one, restrict the builtins. I shipped the default set, which left `http.send` reachable from pasted Rego. It fired a request from the visitor's browser and deadlocked the Go runtime for the rest of the session. -Have a go at stage 3 and drag the hour slider: [0-draft.github.io/authz-playground](https://0-draft.github.io/authz-playground/) +Have a go at stage 3 and drag across the chart: [0-draft.github.io/authz-playground](https://0-draft.github.io/authz-playground/)