Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added articles/assets/authz-playground/day.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added articles/assets/authz-playground/graphs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed articles/assets/authz-playground/map.png
Binary file not shown.
Binary file removed articles/assets/authz-playground/proj.png
Binary file not shown.
21 changes: 15 additions & 6 deletions articles/authz-playground.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -32,26 +32,35 @@ 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.

Comment thread
kanywst marked this conversation as resolved.
![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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence compared the four engines' policy shapes and leaned on the proj.png side-by-side image ("The same rules side by side…"). Both the lead-in and the image are gone. It now sits under the graphs image, which shows only the classic and conditional ReBAC models at 08:00, so the Casbin remark has nothing to point at. Either restore a comparison or fold the sentence into the surrounding prose.


## 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.

I gave the ReBAC model `viewer: owner or editor`, because obviously an editor can read too. The requirements never said that. Disagreements doubled and the comparison was quietly dishonest until the harness flagged it.

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/)
Loading